Oracle分区表 (二)

三. 分区表的其他操作

3.1 添加新的分区

添加新的分区有2中情况:

(1)原分区里边界是maxvalue或者default。 这种情况下,我们需要把边界分区drop掉,加上新分区后,在添加上新的分区。 或者采用split,对边界分区进行拆分。

(2)没有边界分区的。 这种情况下,直接添加分区就可以了。

 

以边界分区添加新分区示例:

(1)分区表和索引的信息如下:

SQL> create table custaddr

  2  (

  3    id         varchar2(15 byte)   not null,

  4    areacode   varchar2(4 byte)

  5  )

  6  partition by list (areacode)

  7  (

  8    partition t_list556 values ('556') tablespace icd_service,

  9    partition p_other values (default)tablespace icd_service

 10  );

表已创建。

SQL> create index ix_custaddr_id on custaddr(id)

  2  local (

  3    partition t_list556  tablespace icd_service,

  4    partition p_other tablespace icd_service

  5  );

索引已创建。

 

(2)插入几条测试数据:

SQL> insert into custaddr values('1','556');

已创建 1 行。

SQL> insert into custaddr values('2','551');

已创建 1 行。

SQL> insert into custaddr values('3','555');

已创建 1 行。

SQL> commit;

提交完成。

SQL> select * from custaddr;

ID              AREA

--------------- ----

1               556

2               551

3               555

SQL> select * from custaddr partition(t_list556);

ID              AREA

--------------- ----

1               556

SQL>

 

(3)删除default分区

sql> alter table custaddr drop partition p_other;

表已更改。

sql> select table_name,partition_name from user_tab_partitions where table_name='CUSTADDR';

table_name                     partition_name

------------------------------ ------------------------------

custaddr                       t_list556

 

(4)添加新分区

SQL> alter table custaddr add partition t_list551 values('551') tablespace icd_service;

表已更改。

SQL> select table_name,partition_name from user_tab_partitions where table_name='CUSTADDR';

 

TABLE_NAME                     PARTITION_NAME

------------------------------ ------------------------------

CUSTADDR                       T_LIST556

CUSTADDR                       T_LIST551

(5)添加default 分区

SQL> alter table custaddr add partition p_other values (default)  tablespace icd_service;

表已更改。

SQL> select table_name,partition_name from user_tab_partitions where table_name='CUSTADDR';

 

TABLE_NAME                     PARTITION_NAME

------------------------------ ------------------------------

CUSTADDR                       T_LIST556

CUSTADDR                       T_LIST551

CUSTADDR                       P_OTHER

 

(6)对于局部索引,oracle会自动增加一个局部分区索引。验证一下:

sql> select owner,index_name,table_name,partitioning_type from dba_part_indexes where index_name='ix_custaddr_id';

owner            index_name           table_name

---------------------- ------------------------------ ------------------

icd             ix_custaddr_id         custaddr

sql> select index_owner,index_name,partition_name from dba_ind_partitions  where index_name='ix_custaddr_id';

index_owner           index_name                  partition_name

------------------------------ ------------------------------ ------------------

icd                  ix_custaddr_id                 p_other

icd                  ix_custaddr_id                 t_list551

icd                  ix_custaddr_id                 t_list556

 

分区索引自动创建了。

 

3.2  split 分区拆分

       在3.1 中,我们说明了可以使用split的方式来添加分区。 这里我们用split方法继续上面的实验。

 

sql> alter table custaddr split partition p_other values('552') into (partition t_list552 tablespace icd_service, partition p_other tablespace icd_service);

表已更改。

--注意这里红色的地方,如果是Range类型的,使用at,List使用Values。

SQL> select table_name,partition_name from user_tab_partitions where table_name='CUSTADDR';

TABLE_NAME                     PARTITION_NAME

------------------------------ ------------------------------

CUSTADDR                       T_LIST556

CUSTADDR                       T_LIST551

CUSTADDR                       T_LIST552

CUSTADDR                       P_OTHER

 

SQL> select index_owner,index_name,partition_name from dba_ind_partitions  where index_name='IX_CUSTADDR_ID';

 

index_owner             index_name                     partition_name

------------------------------ ------------------------------ ------------------

icd                            ix_custaddr_id                 p_other

icd                            ix_custaddr_id                 t_list551

icd                            ix_custaddr_id                 t_list552

icd                            ix_custaddr_id                 t_list556

 

注意:分区表会自动维护局部分区索引。全局索引会失效,需要进行rebuild。

 

3.3 合并分区Merge

       相邻的分区可以merge为一个分区,新分区的下边界为原来边界值较低的分区,上边界为原来边界值较高的分区,原先的局部索引相应也会合并,全局索引会失效,需要rebuild。

 

SQL> alter table custaddr merge partitions t_list552,p_other into partition p_other;

表已更改。

SQL> select index_owner,index_name,partition_name from dba_ind_partitions  where index_name='IX_CUSTADDR_ID';

index_owner       index_name         partition_name

--------------------  ------------------------------ ------------------

icd              ix_custaddr_id          p_other

icd              ix_custaddr_id          t_list551

icd              ix_custaddr_id          t_list556

 

SQL> select table_name,partition_name from user_tab_partitions where table_name='CUSTADDR';

 

table_name                     partition_name

------------------------------ ------------------------------

custaddr                       t_list556

custaddr                       t_list551

custaddr                       p_other

 

3.4 . 移动分区

SQL> alter table custaddr move partition P_OTHER tablespace system;

 

表已更改。

SQL> alter table custaddr move partition P_OTHER tablespace icd_service;

表已更改。

 

注意:分区移动会自动维护局部分区索引,oracle不会自动维护全局索引,所以需要我们重新rebuild分区索引,具体需要rebuild哪些索引,可以通过dba_part_indexes,dba_ind_partitions去判断。

 

SQL> Select index_name,status From user_indexes Where table_name='CUSTADDR';

 

INDEX_NAME                     STATUS

------------------------------ --------

IX_CUSTADDR_ID                 N/A

 

      

3.5. Truncate分区

SQL> select * from custaddr partition(T_LIST556);

ID              AREA

--------------- ----

1               556

SQL> alter table custaddr truncate partition(T_LIST556);

表被截断。

SQL> select * from custaddr partition(T_LIST556);

未选定行

 

说明:

       Truncate相对delete操作很快,数据仓库中的大量数据的批量数据加载可能会有用到;截断分区同样会自动维护局部分区索引,同时会使全局索引unusable,需要重建

 

3.6.  Drop分区

SQL> alter table custaddr drop partition T_LIST551;

表已更改。

 

SQL> select table_name,partition_name from user_tab_partitions where table_name='CUSTADDR';

TABLE_NAME                     PARTITION_NAME

------------------------------ ------------------------------

CUSTADDR                       T_LIST556

CUSTADDR                       P_OTHER     

同样会自动维护局部分区索引,同时会使全局索引unusable,需要重建

 

 

四. 分区表的索引

       分区索引分为本地(local index)索引和全局索引(global index)。局部索引比全局索引容易管理, 而全局索引比较快。

 

与索引有关的表:

       dba_part_indexes 分区索引的概要统计信息,可以得知每个表上有哪些分区索引,分区索引的类型(local/global)

       dba_ind_partitions  每个分区索引的分区级统计信息

       dba_indexes/dba_part_indexes 可以得到每个表上有哪些非分区索引

 

       Local索引肯定是分区索引,Global索引可以选择是否分区,如果分区,只能是有前缀的分区索引。

 

分区索引分2类:有前缀(prefix)的分区索引和无前缀(nonprefix)的分区索引:

(1)有前缀的分区索引指包含了分区键,并且将其作为引导列的索引。

如:

create index i_id_global on PDBA(id) global  --引导列

  2  partition by range(id)  --分区键

  3  (partition p1 values less than (200),

  4  partition p2 values less than (maxvalue)

  5  );

这里的ID 就是分区键,并且分区键id 也是索引的引导列。

 

(2)无前缀的分区索引的列不是以分区键开头,或者不包含分区键列。

如:

create index ix_custaddr_local_id_p on custaddr(id)

local ( 

  partition t_list556 tablespace icd_service, 

  partition p_other tablespace icd_service

)   

 

       这个分区是按照areacode来的。但是索引的引导列是ID。 所以它就是非前缀分区索引。

 

全局分区索引不支持非前缀的分区索引,如果创建,报错如下:

SQL> create index i_time_global on PDBA(id) global  --索引引导列

  2  partition by range(time) --分区建

  3  (partition p1 values less than (TO_DATE('2010-12-1', 'YYYY-MM-DD')),

  4  partition p2 values less than (maxvalue)

  5  );

partition by range(time)

                       *

第 2 行出现错误:

ORA-14038: GLOBAL 分区索引必须加上前缀

 

 

4.1  Local 本地索引

       对于local索引,当表的分区发生变化时,索引的维护由Oracle自动进行。

 

注意事项:

(1) 局部索引一定是分区索引,分区键等同于表的分区键。

(2) 前缀和非前缀索引都可以支持索引分区消除,前提是查询的条件中包含索引分区键。

(3) 局部索引只支持分区内的唯一性,无法支持表上的唯一性,因此如果要用局部索引去给表做唯一性约束,则约束中必须要包括分区键列。

(4) 局部分区索引是对单个分区的,每个分区索引只指向一个表分区;全局索引则不然,一个分区索引能指向n个表分区,同时,一个表分区,也可能指向n个索引分区,对分区表中的某个分区做truncate或者move,shrink等,可能会影响到n个全局索引分区,正因为这点,局部分区索引具有更高的可用性。

(5) 位图索引必须是局部分区索引。

(6) 局部索引多应用于数据仓库环境中。

(7) B树索引和位图索引都可以分区,但是HASH索引不可以被分区。

 

 

示例:

sql> create index ix_custaddr_local_id on custaddr(id) local;

索引已创建。

 

和下面SQL 效果相同,因为local索引就是分区索引:

create index ix_custaddr_local_id_p on custaddr(id)

local ( 

  partition t_list556 tablespace icd_service, 

  partition p_other tablespace icd_service

)   

 

SQL> create index ix_custaddr_local_areacode on custaddr(areacode) local;

索引已创建。

 

验证2个索引的类型:

SQL> select index_name,table_name,partitioning_type,locality,ALIGNMENT from user_part_indexes where table_name='CUSTADDR';

 

index_name                table_name partition locali alignment

------------------------------ ---------- --------- ------ ------------

ix_custaddr_local_areacode     custaddr   list      local  prefixed

ix_custaddr_local_id           custaddr   list      local  non_prefixed

 

       因为我们的custaddr表是按areacode进行分区的,所以索引ix_custaddr_local_areacode是有前缀的索引(prefixed)。而ix_custaddr_local_id是非前缀索引。

 

4.2  Global索引

       对于global索引,可以选择是否分区,而且索引的分区可以不与表分区相对应。全局分区索引只能是B树索引,到目前为止(10gR2),oracle只支持有前缀的全局索引。

       另外oracle不会自动的维护全局分区索引,当我们在对表的分区做修改之后,如果对分区进行维护操作时不加上update global indexes的话,通常会导致全局索引的INVALDED,必须在执行完操作后 REBUILD。      

 

注意事项:

(1)全局索引可以分区,也可以是不分区索引,全局索引必须是前缀索引,即全局索引的索引列必须是以索引分区键作为其前几列。

(2)全局索引可以依附于分区表;也可以依附于非分区表。

(3)全局分区索引的索引条目可能指向若干个分区,因此,对于全局分区索引,即使只截断一个分区中的数据,都需要rebulid若干个分区甚至是整个索引。

(4)全局索引多应用于oltp系统中。

(5)全局分区索引只按范围或者散列分区,hash分区是10g以后才支持。

(6) oracle9i以后对分区表做move或者truncate的时可以用update global indexes语句来同步更新全局分区索引,用消耗一定资源来换取高度的可用性。

(7) 表用a列作分区,索引用b做局部分区索引,若where条件中用b来查询,那么oracle会扫描所有的表和索引的分区,成本会比分区更高,此时可以考虑用b做全局分区索引。

 

 

注意:Oracle只支持2中类型的全局分区索引:

           range partitioned 和 Hash Partitioned.

 

官网的说明如下:

Global Partitioned Indexes

       Oracle offers two types of global partitioned index: range partitioned and hash partitioned.

(1)Global Range Partitioned Indexes

       Global range partitioned indexes are flexible in that the degree of partitioning and the partitioning key are independent from the table's partitioning method. They are commonly used for OLTP environments and offer efficient access to any individual record.

       The highest partition of a global index must have a partition bound, all of whose values are MAXVALUE. This ensures that all rows in the underlying table can be represented in the index. Global prefixed indexes can be unique or nonunique.

       You cannot add a partition to a global index because the highest partition always has a partition bound of MAXVALUE. If you wish to add a new highest partition, use the ALTER INDEX SPLIT PARTITION statement. If a global index partition is empty, you can explicitly drop it by issuing the ALTER INDEX DROP PARTITION statement. If a global index partition contains data, dropping the partition causes the next highest partition to be marked unusable. You cannot drop the highest partition in a global index.

(2)Global Hash Partitioned Indexes

       Global hash partitioned indexes improve performance by spreading out contention when the index is monotonically growing. In other words, most of the index insertions occur only on the right edge of an index.

 

(3)Maintenance of Global Partitioned Indexes

       By default, the following operations on partitions on a heap-organized table mark all global indexes as unusable:

ADD (HASH)

COALESCE (HASH)

DROP

EXCHANGE

MERGE

MOVE

SPLIT

TRUNCATE

 

示例1 全局索引,全局索引对所有分区类型都支持:

sql> create index ix_custaddr_ global_id on custaddr(id) global;

索引已创建。

 

示例2:全局分区索引,只支持Range 分区和Hash 分区:

 

(1)创建2个测试分区表:

sql> create table pdba (id number, time date) partition by range (time)

  2  (

  3  partition p1 values less than (to_date('2010-10-1', 'yyyy-mm-dd')),

  4  partition p2 values less than (to_date('2010-11-1', 'yyyy-mm-dd')),

  5  partition p3 values less than (to_date('2010-12-1', 'yyyy-mm-dd')),

  6  partition p4 values less than (maxvalue)

  7  );

表已创建。

 

SQL> create table Thash

  2  (

  3  id number primary key,

  4  item_id number(8) not null

  5  )

  6  partition by hash(id)

  7  (

  8  partition part_01,

  9  partition part_02,

 10  partition part_03

 11  );

 

表已创建。

 

(2)创建分区索引

 

示例2:全局分区索引

 

SQL> create index i_id_global on PDBA(id) global

  2  partition by range(id)

  3  (partition p1 values less than (200),

  4  partition p2 values less than (maxvalue)

  5  );

索引已创建。

--这个是有前缀的分区索引。

 

SQL> create index i_time_global on PDBA(id) global

  2  partition by range(time)

  3  (partition p1 values less than (TO_DATE('2010-12-1', 'YYYY-MM-DD')),

  4  partition p2 values less than (maxvalue)

  5  );

partition by range(time)

                       *

第 2 行出现错误:

ORA-14038: GLOBAL 分区索引必须加上前缀

 

 

SQL> create index i_time_global on PDBA(time) global

  2  partition by range(time)

  3  (partition p1 values less than (TO_DATE('2010-12-1', 'YYYY-MM-DD')),

  4  partition p2 values less than (maxvalue)

  5  );

索引已创建。

--有前缀的分区索引

 

SQL> select index_name,table_name,partitioning_type,locality,ALIGNMENT from user_part_indexes where table_name='PDBA';

index_name            table_name partition locali alignment

------------------------------ ---------- --------- ------ ------------

i_id_global             pdba       range     global prefixed

i_time_global           pdba       range     global prefixed

 

SQL> CREATE INDEX ix_hash ON PDBA (id,time) GLOBAL

  2       PARTITION BY HASH (id)

  3       (PARTITION p1,

  4        PARTITION p2,

  5        PARTITION p3,

  6        PARTITION p4);

索引已创建。

 

只要索引的引导列包含分区键,就是有前缀的分区索引。

4.3 索引重建问题

(1)分区索引

       对于分区索引,不能整体进行重建,只能对单个分区进行重建。语法如下:

       Alter index idx_name rebuild partition index_partition_name [online nologging]

说明:

       online:表示重建的时候不会锁表。

       nologging:表示建立索引的时候不生成日志,加快速度。

      

如果要重建分区索引,只能drop表原索引,在重新创建:

       SQL>create index loc_xxxx_col on xxxx(col) local tablespace SYSTEM;

       这个操作要求较大的临时表空间和排序区。

 

示例:

SQL> select index_name,partition_name from user_ind_partitions where index_name='I_TIME_GLOBAL';

INDEX_NAME                     PARTITION_NAME

------------------------------ ------------------------------

I_TIME_GLOBAL                  P1

I_TIME_GLOBAL                  P2

SQL>  alter index I_TIME_GLOBAL rebuild partition p1 online nologging;

索引已更改。

SQL> alter index I_TIME_GLOBAL rebuild partition p2 online nologging;

索引已更改。

(2)全局索引

       Oracle 会自动维护分区索引,对于全局索引,如果在对分区表操作时,没有指定update  index,则会导致全局索引失效,需要重建。

SQL> select owner,index_name,table_name,status from dba_indexes where INDEX_NAME='IX_PDBA_GLOBAL';

 owner         index_name                     table_name status

------------------------------ ------------------------------ ---------- -------

sys           ix_pdba_global                 pdba       valid

删除一个分区:

SQL> alter table pdba drop partition p2;

表已更改。

SQL> select owner,index_name,table_name,status from dba_indexes where INDEX_NAME='IX_PDBA_GLOBAL';

owner            index_name          table_name status

------------------------------ ------------------------------ ---------- -------

sys             ix_pdba_global         pdba       valid

split 分区:

SQL> alter table pdba split partition P4 at(TO_DATE('2010-12-21 00:00:00','YYYY-MM-DD HH24:MI:SS')) into (partition P4, partition P5);

表已更改。

SQL> select owner,index_name,table_name,status from dba_indexes where INDEX_NAME='IX_PDBA_GLOBAL';

owner        index_name                     table_name status

------------------------------ ------------------------------ ---------- -------

sys          ix_pdba_global                 pdba       valid

drop 分区时使用update indexes

SQL> alter table pdba drop partition P4 UPDATE INDEXES;

表已更改。

SQL> select owner,index_name,table_name,status from dba_indexes where INDEX_NAME='IX_PDBA_GLOBAL';

owner          index_name           table_name status

---------------------- ------------------------------ ---------- -------

sys             ix_pdba_global        pdba       valid

做了几个drop分区操作,全局索引没有失效,有点奇怪。 不过如果在生产环境中,还是小心点。

 

重建全局索引命令如下:

       Alter index idx_name rebuild [online nologging]

 

示例:

SQL> Alter index ix_pdba_global rebuild online nologging;

索引已更改。

补充一点,分区表存储空间的问题:
SQL> select table_name,partition_name,tablespace_name from user_tab_partitions where table_name='DBA';

TABLE_NAME PARTITION_NAME                 TABLESPACE_NAME
---------- ------------------------------ ------------------------------
DBA        P1                             SYSTEM
DBA        P2                             SYSTEM
DBA        P3                             SYSTEM
DBA        P4                             SYSTEM

 

通过user_tab_partitions 表可以查看到每个分区对应的tablesapce_name. 但是,如果通过all_tables 表,却查不到分区表对应表空间的信息。

分区表:
SQL> select owner,table_name,tablespace_name,cluster_name from all_tables where table_name='DBA';

OWNER TABLE_NAME TABLESPACE_NAME                CLUSTER_NAME
----- ---------- ------------------------------ -----------------------------------------------------
SYS   DBA

普通表:
SQL> select owner,table_name,tablespace_name,cluster_name from all_tables where table_name='DAVE';

OWNER TABLE_NAME TABLESPACE_NAME                CLUSTER_NAME
----- ---------- ------------------------------ ---------------------------------------------------
SYS   DAVE       SYSTEM


 PS:

       在google的时候,发现斯坦福大学(http://stanford.edu/)的文档库上有很多Oracle资料。

       国内这块其他大学没有留意过,中国科学技术大学(http://www.ustc.edu.cn/)也是有资料库的

你可能感兴趣的:(Oracle分区表 (二))