一. 分区表理论知识
Oracle提供了分区技术以支持VLDB(Very Large DataBase)。分区表经过对分区列的判断,把分区列不一样的记录,放到不一样的分区中。分区彻底对应用透明。
Oracle的分区表能够包括多个分区,每一个分区都是一个独立的段(SEGMENT),能够存放到不一样的表空间中。查询时能够经过查询表来访问各个分区中的数据,也能够经过在查询时直接指定分区的方法来进行查询。 sql
When to Partition a Table何时须要分区表,官网的2个建议以下: shell
- Tables greater than 2GB should always be considered for partitioning.
- Tables containing historical data, in which new data is added into the newest partition. A typical example is a historical table where only the current month's data is updatable and the other 11 months are read only.
在oracle 10g中最多支持:1024k-1个分区:
Tables can be partitioned into up to 1024K-1 separate partitions
联机文档上有关分区表和索引的说明:
Partitioned Tables and Indexes
http://download.oracle.com/docs/cd/B19306_01/server.102/b14220/partconc.htm#sthref2604 session
分区提供如下优势:
(1)因为将数据分散到各个分区中,减小了数据损坏的可能性;
(2)能够对单独的分区进行备份和恢复;
(3)能够将分区映射到不一样的物理磁盘上,来分散IO;
(4)提升可管理性、可用性和性能。 oracle
Oracle 10g提供了如下几种分区类型:
(1)范围分区(range);
(2)哈希分区(hash);
(3)列表分区(list);
(4)范围-哈希复合分区(range-hash);
(5)范围-列表复合分区(range-list)。 app
Range分区:
Range分区是应用范围比较广的表分区方式,它是以列的值的范围来作为分区的划分条件,将记录存放到列值所在的range分区中。
如按照时间划分,2010年1月的数据放到a分区,2月的数据放到b分区,在建立的时候,须要指定基于的列,以及分区的范围值。
在按时间分区时,若是某些记录暂没法预测范围,能够建立maxvalue分区,全部不在指定范围内的记录都会被存储到maxvalue所在分区中。
如: less
create table pdba (id number, time date) partition by range (time) ( partition p1 values less than (to_date('2010-10-1', 'yyyy-mm-dd')), partition p2 values less than (to_date('2010-11-1', 'yyyy-mm-dd')), partition p3 values less than (to_date('2010-12-1', 'yyyy-mm-dd')), partition p4 values less than (maxvalue) )
Hash分区:
对于那些没法有效划分范围的表,可使用hash分区,这样对于提升性能仍是会有必定的帮助。hash分区会将表中的数据平均分配到你指定的几个分区中,列所在分区是依据分区列的hash值自动分配,所以你并不能控制也不知道哪条记录会被放到哪一个分区中,hash分区也能够支持多个依赖列。
如: ide
create table test ( transaction_id number primary key, item_id number(8) not null ) partition by hash(transaction_id) ( partition part_01 tablespace tablespace01, partition part_02 tablespace tablespace02, partition part_03 tablespace tablespace03 );
在这里,咱们指定了每一个分区的表空间。 性能
List分区:
List分区也须要指定列的值,其分区值必须明确指定,该分区列只能有一个,不能像range或者hash分区那样同时指定多个列作为分区依赖列,但它的单个分区对应值能够是多个。
在分区时必须肯定分区列可能存在的值,一旦插入的列值不在分区范围内,则插入/更新就会失败,所以一般建议使用list分区时,要建立一个default分区存储那些不在指定范围内的记录,相似range分区中的maxvalue分区。
在根据某字段,如城市代码分区时,能够指定default,把非分区规则的数据,所有放到这个default分区。
如: 测试
create table custaddr ( id varchar2(15 byte) not null, areacode varchar2(4 byte) ) partition by list (areacode) ( partition t_list025 values ('025'), partition t_list372 values ('372') , partition t_list510 values ('510'), partition p_other values (default) )
组合分区:
若是某表按照某列分区以后,仍然较大,或者是一些其它的需求,还能够经过分区内再建子分区的方式将分区再分区,即组合分区的方式。
组合分区呢在10g中有两种:range-hash,range-list。注意顺序,根分区只能是range分区,子分区能够是hash分区或list分区。
如: 大数据
create table test ( transaction_id number primary key, transaction_date date ) partition by range(transaction_date) subpartition by hash(transaction_id) subpartitions 3 store in (tablespace01,tablespace02,tablespace03) ( partition part_01 values less than(to_date(’2009-01-01’,’yyyy-mm-dd’)), partition part_02 values less than(to_date(’2010-01-01’,’yyyy-mm-dd’)), partition part_03 values less than(maxvalue) ); create table emp_sub_template (deptno number, empname varchar(32), grade number) partition by range(deptno) subpartition by hash(empname) subpartition template (subpartition a tablespace ts1, subpartition b tablespace ts2, subpartition c tablespace ts3, subpartition d tablespace ts4 ) (partition p1 values less than (1000), partition p2 values less than (2000), partition p3 values less than (maxvalue) ); create table quarterly_regional_sales (deptno number, item_no varchar2(20), txn_date date, txn_amount number, state varchar2(2)) tablespace ts4 partition by range (txn_date) subpartition by list (state) (partition q1_1999 values less than (to_date('1-apr-1999','dd-mon-yyyy')) (subpartition q1_1999_northwest values ('or', 'wa'), subpartition q1_1999_southwest values ('az', 'ut', 'nm'), subpartition q1_1999_northeast values ('ny', 'vm', 'nj'), subpartition q1_1999_southeast values ('fl', 'ga'), subpartition q1_1999_northcentral values ('sd', 'wi'), subpartition q1_1999_southcentral values ('ok', 'tx') ), partition q2_1999 values less than ( to_date('1-jul-1999','dd-mon-yyyy')) (subpartition q2_1999_northwest values ('or', 'wa'), subpartition q2_1999_southwest values ('az', 'ut', 'nm'), subpartition q2_1999_northeast values ('ny', 'vm', 'nj'), subpartition q2_1999_southeast values ('fl', 'ga'), subpartition q2_1999_northcentral values ('sd', 'wi'), subpartition q2_1999_southcentral values ('ok', 'tx') ), partition q3_1999 values less than (to_date('1-oct-1999','dd-mon-yyyy')) (subpartition q3_1999_northwest values ('or', 'wa'), subpartition q3_1999_southwest values ('az', 'ut', 'nm'), subpartition q3_1999_northeast values ('ny', 'vm', 'nj'), subpartition q3_1999_southeast values ('fl', 'ga'), subpartition q3_1999_northcentral values ('sd', 'wi'), subpartition q3_1999_southcentral values ('ok', 'tx') ), partition q4_1999 values less than ( to_date('1-jan-2000','dd-mon-yyyy')) (subpartition q4_1999_northwest values ('or', 'wa'), subpartition q4_1999_southwest values ('az', 'ut', 'nm'), subpartition q4_1999_northeast values ('ny', 'vm', 'nj'), subpartition q4_1999_southeast values ('fl', 'ga'), subpartition q4_1999_northcentral values ('sd', 'wi'), subpartition q4_1999_southcentral values ('ok', 'tx') ) );
在Oracle 11g中,组合分区功能这块有所加强,又增长了range-range,list-range,
list-list,list-hash,而且 11g里面还支持Interval分区和虚拟列分区。
这块能够参考Blog:
Oracle 11g 新特性简介
http://blog.csdn.net/tianlesoftware/archive/2010/01/06/5134819.aspx
分区表 之 Interval分区 和 虚拟列 按星期分区表
http://blog.csdn.net/tianlesoftware/archive/2010/06/10/5662337.aspx
二. 普通表转分区表方法
将普通表转换成分区表有4种方法:
- Export/import method
- Insert with a subquery method
- Partition exchange method
- DBMS_REDEFINITION
具体参考:
How to Partition a Non-partitioned Table [ID 1070693.6]
http://blog.csdn.net/tianlesoftware/archive/2011/03/02/6218704.aspx
逻辑导出导入这里就不作说明,咱们看看其余三种方法。
2.1 插入: Insert with a subquery method
这种方法就是使用insert 来实现。 固然在建立分区表的时候能够一块儿插入数据,也能够建立好后在insert 进去。 这种方法采用DDL语句,不产生UNDO,只产生少许REDO,建表完成后数据已经在分布到各个分区中。
SQL> select count(*) from dba; COUNT(*) ---------- 2713235 SQL> alter session set nls_date_format='yyyy-mm-dd hh24:mi:ss';
会话已更改。
SQL> select time_fee from dba where rownum<5;
TIME_FEE
------------------- 2011-02-17 19:29:09 2011-02-17 19:29:15 2011-02-17 19:29:18 2011-02-17 19:29:20 SQL>
2.1.1 Oracle 11g的Interval
在11g里的Interval建立,这种方法对没有写全的分区会自动建立。 好比我这里只写了1月日期,若是插入的数据有其余月份的,会自动生成对应的分区。
/* Formatted on 2011/03/02 15:41:09 (QP5 v5.115.810.9015) */ CREATE TABLE intervaldave PARTITION BY RANGE (time_fee) INTERVAL ( NUMTOYMINTERVAL (1, 'MONTH') ) (PARTITION part1 VALUES LESS THAN (TO_DATE ('01/12/2010', 'MM/DD/YYYY'))) AS SELECT ID, TIME_FEE FROM DAVE;
SQL> select table_name,partition_name from user_tab_partitions where table_name='INTERVALDAVE';
TABLE_NAME PARTITION_NAME
------------------------------ ------------------------------
INTERVALDAVE PART1
INTERVALDAVE SYS_P24
INTERVALDAVE SYS_P25
INTERVALDAVE SYS_P26
INTERVALDAVE SYS_P33
INTERVALDAVE SYS_P27
INTERVALDAVE SYS_P28
2.1.2 Oracle 10g 版本
在10g里面,我须要写全全部的分区。
sql> create table pdba (id, time) partition by range (time) 2 (partition p1 values less than (to_date('2010-10-1', 'yyyy-mm-dd')), 3 partition p2 values less than (to_date('2010-11-1', 'yyyy-mm-dd')), 4 partition p3 values less than (to_date('2010-12-1', 'yyyy-mm-dd')), 5 partition p4 values less than (maxvalue)) 6 as select id, time_fee from dba;
表已建立。
SQL> select table_name,partition_name from user_tab_partitions where table_name='PDBA';
TABLE_NAME PARTITION_NAME
------------------------------ ------------------------------
PDBA P1
PDBA P2
PDBA P3
PDBA P4
sql> select count(*) from pdba partition (p1); count(*) ---------- 1718285 sql> select count(*) from pdba partition (p2); count(*) ---------- 183667 sql> select count(*) from pdba partition (p3); count(*) ---------- 188701 sql> select count(*) from pdba partition (p4); count(*) ---------- 622582 sql>
如今分区表已经建好了,可是表名不同,须要用rename对表重命名一下:
SQL> rename dba to dba_old;
表已重命名。
SQL> rename pdba to dba;
表已重命名。
SQL> select table_name,partition_name from user_tab_partitions where table_name='DBA';
TABLE_NAME PARTITION_NAME
------------------------------ ------------------------------
DBA P1
DBA P2
DBA P3
DBA P4
2.2 . 交换分区:Partition exchange method
这种方法只是对数据字典中分区和表的定义进行了修改,没有数据的修改或复制,效率最高。适用于包含大数据量的表转到分区表中的一个分区的操做。尽可能在闲时进行操做。
交换分区的操做步骤以下:
- 建立分区表,假设有2个分区,P1,P2.
- 建立表A存放P1规则的数据。
- 建立表B 存放P2规则的数据。
- 用表A 和P1 分区交换。 把表A的数据放到到P1分区
- 用表B 和p2 分区交换。 把表B的数据存放到P2分区。
建立分区表:
sql> create table p_dba 2 (id number,time date) 3 partition by range(time) 4 ( 5 partition p1 values less than (to_date('2010-09-1', 'yyyy-mm-dd')), 6 partition p2 values less than (to_date('2010-11-1', 'yyyy-mm-dd')) 7 );
表已建立。
注意:我这里只建立了2个分区,没有建立存放其余数据的分区。
建立2个分别对应分区的基表:
SQL> CREATE TABLE dba_p1 as SELECT id,time_fee FROM dba_old WHERE time_fee<TO_DATE('2010-09-1', 'YYYY-MM-DD');
表已建立。
SQL> CREATE TABLE dba_p2 as SELECT id,time_fee FROM dba_old WHERE time_fee<TO_DATE('2010-11-1', 'YYYY-MM-DD') and time_fee>TO_DATE('2010-09-1', 'YYYY-MM-DD');
表已建立。
SQL> select count(*) from dba_p1; COUNT(*) ---------- 1536020 SQL> select count(*) from dba_p2; COUNT(*) ---------- 365932 SQL>
讲2个基表与2个分区进行交换:
SQL> alter table p_dba exchange partition p1 with table dba_p1;
表已更改。
SQL> alter table p_dba exchange partition p2 with table dba_p2;
表已更改。
查询2个分区:
SQL> select count(*) from p_dba partition(p1); COUNT(*) ---------- 1536020 SQL> select count(*) from p_dba partition(p2); COUNT(*) ---------- 365932 注意:数据和以前的基表一致。
查询原来的2个基表:
SQL> select count(*) from dba_p2; COUNT(*) ---------- 0 SQL> select count(*) from dba_p1; COUNT(*) ---------- 0
注意: 2个基表的数据变成成0。
在这里咱们看一个问题,通常状况下,咱们在建立分区表的时候,都会有一个其余分区,用来存放不匹配分区规则的数据。 在这个例子中,我只建立了2个分区,没有建立maxvalue分区。 如今我来插入一条不知足规则的数据,看结果:
SQL> insert into p_dba values(999999,to_date('2012-12-29','yyyy-mm-dd')); insert into p_dba values(999999,to_date('2012-12-29','yyyy-mm-dd')) * 第 1 行出现错误: ORA-14400: 插入的分区关键字未映射到任何分区 SQL> insert into p_dba values(999999,to_date('2009-12-29','yyyy-mm-dd'));
已建立 1 行。
SQL> select * from p_dba where id=999999;
ID TIME
---------- -------------- 999999 29-12月-09 SQL> alter session set nls_date_format='yyyy-mm-dd hh24:mi:ss';
会话已更改。
SQL> select * from p_dba where id=999999;
ID TIME
---------- ------------------- 999999 2009-12-29 00:00:00 SQL>
经过这个测试能够清楚,若是插入的数据不知足分区规则,会报ORA-14400错误。
2.3 . 使用在线重定义:DBMS_REDEFINITION
在线重定义能保证数据的一致性,在大部分时间内,表均可以正常进行DML操做。只在切换的瞬间锁表,具备很高的可用性。这种方法具备很强的灵活性,对各类不一样的须要都能知足。并且,能够在切换前进行相应的受权并创建各类约束,能够作到切换完成后再也不须要任何额外的管理操做。
关于DBMS_REDEFINITION的介绍,参考官方链接:
http://download.oracle.com/docs/cd/B19306_01/appdev.102/b14258/d_redefi.htm#CBBFDJBC
关于用在线重定义建立分区表,参考:
How To Partition Existing Table Using DBMS_Redefinition [ID 472449.1]
http://blog.csdn.net/tianlesoftware/archive/2011/03/02/6218693.aspx
这个功能只在9.2.0.4之后的版本才有,在线重定义表具备如下功能:
(1)修改表的存储参数;
(2)将表转移到其余表空间;
(3)增长并行查询选项;
(4)增长或删除分区;
(5)重建表以减小碎片;
(6)将堆表改成索引组织表或相反的操做;
(7)增长或删除一个列。
使用在线重定义的一些限制条件:
(1) There must be enough space to hold two copies of the table.
(2) Primary key columns cannot be modified.
(3) Tables must have primary keys.
(4) Redefinition must be done within the same schema.
(5) New columns added cannot be made NOT NULL until after the redefinition operation.
(6) Tables cannot contain LONGs, BFILEs or User Defined Types.
(7) Clustered tables cannot be redefined.
(8) Tables in the SYS or SYSTEM schema cannot be redefined.
(9) Tables with materialized view logs or materialized views defined on them cannot be redefined.
(10) Horizontal sub setting of data cannot be performed during the redefinition.
在Oracle 10.2.0.4和11.1.0.7 版本下,在线重定义可能会遇到以下bug:
Bug 7007594 - ORA-600 [12261]
http://blog.csdn.net/tianlesoftware/archive/2011/03/02/6218681.aspx
在线重定义的大体操做流程以下:
(1)建立基础表A,若是存在,就不须要操做。
(2)建立临时的分区表B。
(3)开始重定义,将基表A的数据导入临时分区表B。
(4)结束重定义,此时在DB的 Name Directory里,已经将2个表进行了交换。即此时基表A成了分区表,咱们建立的临时分区表B 成了普通表。 此时咱们能够删除咱们建立的临时表B。它已是普通表。
下面看一个示例:
-
建立基本表和索引
sql> conn icd/icd;
已链接。
sql> create table unpar_table ( 2 id number(10) primary key, 3 create_date date 4 );
表已建立。
sql> insert into unpar_table select rownum, created from dba_objects;
已建立72288行。
sql> create index create_date_ind on unpar_table(create_date);
索引已建立。
sql> commit;
提交完成。
-
收集表的统计信息
sql> exec dbms_stats.gather_table_stats('icd', 'unpar_table', cascade => true);
pl/sql 过程已成功完成。
-
建立临时分区表
sql> create table par_table (id number primary key, time date) partition by range (time) 2 (partition p1 values less than (to_date('2004-7-1', 'yyyy-mm-dd')), 3 partition p2 values less than (to_date('2005-1-1', 'yyyy-mm-dd')), 4 partition p3 values less than (to_date('2005-7-1', 'yyyy-mm-dd')), 5 partition p4 values less than (maxvalue));
表已建立。
- 进行重定义操做
4.1 检查重定义的合理性
sql> exec dbms_redefinition.can_redef_table('icd', 'unpar_table');
pl/sql 过程已成功完成。
4.2 若是4.1 没有问题,开始重定义,这个过程可能要等一会。
这里要注意:若是分区表和原表列名相同,能够用以下方式进行:
SQL> BEGIN
DBMS_REDEFINITION.start_redef_table(
uname => 'ICD',
orig_table => 'unpar_table',
int_table => 'par_table');
END;
/
若是分区表的列名和原表不一致,那么在开始重定义的时候,须要从新指定映射关系:
SQL> EXEC DBMS_REDEFINITION.START_REDEF_TABLE(
'ICD',
'unpar_table',
'par_table',
'ID ID, create_date TIME', -- 在这里指定新的映射关系
DBMS_REDEFINITION.CONS_USE_PK);
这一步操做结束后,数据就已经同步到这个临时的分区表里来了。
4.3 同步新表,这是可选的操做
SQL> BEGIN
2 dbms_redefinition.sync_interim_table(
3 uname => 'ICD',
4 orig_table => 'unpar_table',
5 int_table => 'par_table');
6 END;
7 /
PL/SQL 过程已成功完成。
4.4 建立索引,在线重定义只重定义数据,索引还须要单独创建。
sql> create index create_date_ind2 on par_table(time);
索引已建立。
4.5 收集新表的统计信息
sql> exec dbms_stats.gather_table_stats('icd', 'par_table', cascade => true);
pl/sql 过程已成功完成。
4.6 结束重定义
SQL> BEGIN
2 dbms_redefinition.finish_redef_table(
3 uname => 'ICD',
4 orig_table => 'unpar_table',
5 int_table => 'par_table');
6 END;
7 /
PL/SQL 过程已成功完成。
结束重定义的意义:
基表unpar_table 和临时分区表par_table 进行了交换。 此时临时分区表par_table成了普通表,咱们的基表unpar_table成了分区表。
咱们在重定义的时候,基表unpar_table是能够进行DML操做的。 只有在2个表进行切换的时候会有短暂的锁表。
- 删除临时表
SQL> DROP TABLE par_table;
表已删除。
- 索引重命名
SQL> ALTER INDEX create_date_ind2 RENAME TO create_date_ind;
索引已更改。
- 验证
sql> select partitioned from user_tables where table_name = 'UNPAR_TABLE';
par
---
yes
sql> select partition_name from user_tab_partitions where table_name = 'UNPAR_TABLE';
partition_name
------------------------------
p1
p2
p3
p4
sql> select count() from unpar_table;
count()
----------
72288
sql> select count() from unpar_table partition (p4);
count()
----------
72288
sql>
三. 分区表的其余操做
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