首页 > 代码库 > ORACLE 范围分区 partition-range分区
ORACLE 范围分区 partition-range分区
范围分区的用法说明
Range partitioning maps data to partitions based on ranges of partition key values that you establish for each partition. It is the most common type of partitioning and is often used with dates. For example, you might want to partition sales data into monthly
partitions.
When using range partitioning, consider the following rules:
■ Each partition has a VALUES LESS THAN clause, which specifies a noninclusive upper bound for the partitions. Any values of the partition key equal to or higher than this literal are added to the next higher partition.
■ All partitions, except the first, have an implicit lower bound specified by the VALUES LESS THAN clause on the previous partition.
■ A MAXVALUE literal can be defined for the highest partition. MAXVALUE represents a virtual infinite value that sorts higher than any other possible value for the partition key, including the null value.
由此可知,range分区是应用最广的分区方式,它是以列的值的范围来做为分区的划分条件,将记录存放到列值所在的 range 分区中,因此在创建的时候呢,需要你指定基于的列,以及分区的范围值,如果某些记录暂无法预测范围,可以创建 maxvalue 分区,所有不在指定范围内的记录都会被存储到 maxvalue 所在分区中,并且支持多列做为依赖列。每个分区内储存的数据小于该分区指定的values less than数值,除第一个分区外,其它分区都有最小值且等于上一分区指定的values less than数值。
1、创建range分区
语法如下:
column:分区依赖列(如果是多个,以逗号分隔);
partition:分区名称;
values less than:后跟分区范围值(如果依赖列有多个,范围对应值也是多个,以逗号分隔开);
tablespace_clause:分区的存储属性,例如所在表空间等属性(可为空),默认继承基表所有表空间的属性。
Range Partition Example
SQL> edit
已写入 file afiedt.buf
1 create table t_partition_range(id number,name varchar2(20))
2 partition by range(id)(
3 partition p1 values less than(10),
4 partition p2 values less than(20),
5 partition p3 values less than(30),
6 partition pmax values less than(maxvalue)
7* )
SQL> /
表已创建。
通过数据字典“user_part_tables记录分区表的信息”,“user_tab_partitions记录表分区的信息”可查询创建分区的信息。。
例如:
SQL> select table_name,partitioning_type,partition_count
2 from user_part_tables where table_name=‘T_PARTITION_RANGE‘;
TABLE_NAME PARTITI PARTITION_COUNT
------------------------------ ------- ---------------
T_PARTITION_RANGE RANGE 4
SQL> set linesize 200
SQL> column partition_name format a20;
SQL> column high_value format 999;
SQL> column tablespace_name format a30;
SQL> edit
已写入 file afiedt.buf
1 select partition_name,high_value,tablespace_name
2 from user_tab_partitions where table_name=‘T_PARTITION_RANGE‘
3* order by partition_position
SQL> /
PARTITION_NAME HIGH_VALUE TABLESPACE_NAME
-------------------- -------------------------------------------------------------------------------
P1 10 JJJG
P2 20 JJJG
P3 30 JJJG
PMAX MAXVALUE JJJG
2、range分区表上创建索引
2.1、创建global索引range分区:
SQL> edit
已写入 file afiedt.buf
1 create index idx_parti_range_id on t_partition_range(id)
2 global partition by range(id)(
3 partition i_p1 values less than(10),
4 partition i_p2 values less than(20),
5 partition i_p3 values less than(30),
6* partition i_pmax values less than(maxvalue))
SQL> /
索引已创建。
由上例可以看出,创建 global 索引的分区与创建表的分区语句格式完全相同,而且其分区形式与索引 所在表的分区形式没有关联关系。
注:上例是range分区表创建range分区的 global索引,并不表示range分区表只能创建range分区global索引,也可创建hash分区的global索引。
查询索引的分区信息可通过user_part_indexes,user_ind_partitions两个数据字典。
2.2、创建Local分区索引
SQL> create index idx_parti_range_id on t_partition_range(id) local;
索引已创建。
SQL> select index_name,partitioning_type,partition_count
2 from user_part_indexes
3 where index_name=‘IDX_PARTI_RANGE_ID‘;
INDEX_NAME PARTITI PARTITION_COUNT
------------------------------ ------- ---------------
IDX_PARTI_RANGE_ID RANGE 4
SQL> edit
已写入 file afiedt.buf
1 select partition_name,high_value,tablespace_name
2 from user_ind_partitions
3 where index_name=‘IDX_PARTI_RANGE_ID‘
4* order by partition_position
SQL> /
PARTITION_NAME HIGH_VALUE TABLESPACE_NAME
-------------------- -------------------------------------------------------------------------------
P1 10 JJJG
P2 20 JJJG
P3 30 JJJG
PMAX MAXVALUE JJJG
可以看出, local 索引的分区完全继承表的分区的属性,包括分区类型,分区的范围值即不需指定也不
能更改,即local 索引的分区维护完全依赖于其索引所在表,不过分区名称及分区所在的表空间是可以自定义的,如下:
SQL> create index IDX_PART_RANGE_ID ON T_PARTITION_RANGE(id) local (
2 partition i_range_p1 tablespace tbspart01,
3 partition i_range_p2 tablespace tbspart01,
4 partition i_range_p3 tablespace tbspart02,
5 partition i_range_pmax tablespace tbspart02
6 );
3、分区表的管理
3.1、增加表分区(add partition)
增加表分区,适用于所有分区形式,语法为alter table tbname add partition……
值得注意的是像list,range这种存在范围值的分区,所要增加的分区值必须大于当前分区中的最大值(如果当前存在maxvalue或default的
分区,add partition会报错,这种情况只能使用spilt),hash分区则无此限制。
例如:
SQL> alter table t_partition_range add partition p4 values less than(40);
alter table t_partition_range add partition p4 values less than(40)
*
第 1 行出现错误:
ORA-14074: 分区界限必须调整为高于最后一个分区界限。
如前面所说,range分区存在maxvalue时,add partition会报错,下面我们先进行分区拆分。
3.2、拆分表分区(split partition)
split partition即将一个分区拆分成二个,其用途非常广泛,如通常发现一个分区过大,就可以将其分解成多个分区,常最见的还是
split maxvalue/default的分区。
该命令的语法针对不同分区会有不同形式,
For range partition : alter table tbname split partition ptname at (value) into (partition newpt1
tbs_clause,partition newpt2 tbs_clause);
For list partition : alter table tbname split partition ptname values (v1,v2...vn) into (partition newpt1
tbs_clause,partition newpt2 tbs_clause);
上述两项,如果是操作子分区,则将 partition 关键字换成 subpartition 即可。旧分区中符合新定义值的
记录会存储到指定的第一个分区中,其它的记录存储到第二个分区。
例:
查询表现有分区
SQL> edit
已写入 file afiedt.buf
1 select partition_name,high_value,tablespace_name
2 from user_tab_partitions where table_name=‘T_PARTITION_RANGE‘
3* order by partition_position
SQL> /
PARTITION_NAME HIGH_VALUE TABLESPACE_NAME
-------------------- -------------------------------------------------------------------------------
P1 10 JJJG
P2 20 JJJG
P3 30 JJJG
PMAX MAXVALUE JJJG
对range分区表的maxvalue分区进行拆分
SQL> alter table t_partition_range split partition pmax at(40) into(
2 partition p4 ,partition pmax);
表已更改。
拆分后,查询表分区如下
SQL> select partition_name,high_value,tablespace_name
2 from user_tab_partitions where table_name=‘T_PARTITION_RANGE‘
3 order by partition_position;
PARTITION_NAME HIGH_VALUE TABLESPACE_NAME
-------------------- -------------------------------------------------------------------------------
P1 10 JJJG
P2 20 JJJG
P3 30 JJJG
P4 40 JJJG
PMAX MAXVALUE JJJG
即小于40的存放p4分区,其它存放pmax分区。
提示:
1、split partition/subpartition 不能用于 hash 分区或 hash 子分区 (hash 的话,直接用 add partition 就好 了 )
2、split partition/subpartition 视被分隔的分区数据量多少,可能需要花费不小的代价,相当于该分区数据的全扫描,我们也许可以形容为: full partition scan:) ,除非:
Split 后的两个分区中,至少有一个是空的,并且非空的那个分区的存储属性与 split 前的存储属性完全相同 。
如果 split 的分区包含 lob 字段, split 后非空的那个分区中该字段的存储属性也必须与 split 前 的存储属性完全相同。
这种情况下的 split partition/subpartition 也会非常高效, oracle 会自动进行优化,此时的分区操作类 似于 add partition 。
通常情况下,如果在执行 split partition/subpartition 时,如果没有指定 update indexes 子句,都会造成 local 和 global 索引的失效。注意,我们说的是通常,如果你 split partition/subpartition 的是个空分区, 或 者没有触发任何数据移动或变化,那么即使不加 update indexes ,也不会影响到索引。当然,保险起见,建议你还是执行完之后,查询一下数据字典,确认一下当前索引的状态。
3.3、删除表分区(drop partition)
删除表分区包含两种操作,分别是:
删除分区:alter table[tablename] drop partition[ptname];
删除子分区:alter table[tablename] drop subpartition[ptname];
除hash分区和hash子分区外,其它分区格式都可以支持这项操作。
例:删除上面split partition添加的p4分区
SQL> alter table t_partition_range drop partition p4;
表已更改。
SQL> edit
已写入 file afiedt.buf
1 select partition_name,high_value,tablespace_name
2 from user_tab_partitions where table_name=‘T_PARTITION_RANGE‘
3* order by partition_position
SQL> /
PARTITION_NAME HIGH_VALUE TABLESPACE_NAME
-------------------- -------------------------------------------------------------------------------
P1 10 JJJG
P2 20 JJJG
P3 30 JJJG
PMAX MAXVALUE JJJG
注意:删除分区时,该分区内存储的数据也将同时删除。
由于是ddl操作,这种删除比较迅速,因此如果你确认某个分区的数据都要被删除,使用drop partition会比delete更高效,
如果你的本意是希望删除掉指定分区但保留数据,你应该使用merge partition,接下来就会讲到。
同样,如果你在执行该语句时没有指定 update indexes 子句,也会导致 global 索引的失效,至于 local 索引嘛,删除分区时对应的索引分区会被同时删除,但其它分区的local 索引不会受到影响。
3.4、合并表分区(merge partitions)
合并二个分区成一个分区,适用于除hash分区之外的所有分区形式(hash分区此功能用coalesce partition收缩表分区 代替)
语法: alter table tbname merge partitions/subpartitions pt1,pt2 into partition/subpartition pt3;
同样也支持 update indexes 子句以避免单独执行造成索引失效的问题。
注意,要合并的两个分区必须是连续的,合并分区操作不会造成数据丢失,另外如果想为新分区指定属性,在语句末尾处可增加存储属性(
如果不指定,则新分区默认继续表的存储属性)。
例:
--range分区表 t_partition_range先split pmax分区,即新增p4分区
SQL> edit
已写入 file afiedt.buf
1 alter table t_partition_range split partition pmax at(40) into (
2* partition p4,partition pmax)
SQL> /
表已更改。
SQL> edit
已写入 file afiedt.buf
1 select partition_name,high_value,tablespace_name
2 from user_tab_partitions where table_name=‘T_PARTITION_RANGE‘
3* order by partition_position
SQL> /
PARTITION_NAME HIGH_VALUE TABLESPACE_NAME
-------------------- -------------------------------------------------------------------------------
P1 10 JJJG
P2 20 JJJG
P3 30 JJJG
P4 40 JJJG
PMAX MAXVALUE JJJG
--执行merge partition操作
SQL> edit
已写入 file afiedt.buf
1 alter table t_partition_range merge partitions p4,pmax into
2* partition pmax
SQL> /
表已更改。
--查看合并后表分区情况
SQL> edit
已写入 file afiedt.buf
1 select partition_name,high_value,tablespace_name
2 from user_tab_partitions
3 where table_name=‘T_PARTITION_RANGE‘
4* order by partition_position
SQL> /
PARTITION_NAME HIGH_VALUE TABLESPACE_NAME
-------------------- -------------------------------------------------------------------------------
P1 10 JJJG
P2 20 JJJG
P3 30 JJJG
PMAX MAXVALUE JJJG
3.5、交换表分区(Exchange Partitions)
Exchange Partitions就是迁移数据。其 提供了一种方式,让你在表与表或分区与分区之间迁移数据,注意不是将表转换成
分区或非分区的形式,而仅只是迁移表中数据 ( 互相迁移 ) ,由于其号称是采用了更改数据字典的方式,因此效率最高 ( 几乎不涉及 io 操作 ) 。 Exchange partition 适用于所有分区格式,你可以将数据从分区表迁移到非分区表,也可以从非分区表迁移至分区表,或者从 hash partition 到 range partition 诸如此类吧。
其语法很简单: alter table tbname1 exchange partition/subpartition ptname with table tbname2;
例:先给t_partition_range表插入几条数据。
SQL> insert into t_partition_range values(11,‘a‘);
已创建 1 行。
SQL> insert into t_partition_range values(12,‘b‘);
已创建 1 行。
SQL> insert into t_partition_range values(13,‘c‘);
已创建 1 行。
SQL> commit;
提交完成。
SQL> select * from t_partition_range;
ID NAME
---------- --------------------
11 a
12 b
13 c
--创建非分区表,结构与t_partition_range表相同
SQL> create table t_partition_range_tmp(id number,name varchar2(50));
表已创建。
SQL> edit
已写入 file afiedt.buf
1 alter table t_partition_range exchange partition p2
2* with table t_partition_range_tmp
SQL> /
alter table t_partition_range exchange partition p2
*
第 1 行出现错误:
ORA-14097: ALTER TABLE EXCHANGE PARTITION 中的列类型或大小不匹配
--交换失败,因表结构列类型不同,查看二表结构
SQL> desc t_partition_range;
名称 是否为空? 类型
----------------------------------------------------------------------------------- -------- ------
ID NUMBER
NAME VARCHAR2(20)
SQL> desc t_partition_range_tmp;
名称 是否为空? 类型
----------------------------------------------------------------------------------- -------- ------
ID NUMBER
NAME VARCHAR2(50)
--修改t_partition_range_tmp表name字段长度
SQL> alter table t_partition_range_tmp modify name varchar2(20);
表已更改。
SQL> alter table t_partition_range exchange partition p2
2 with table t_partition_range_tmp;
表已更改。
SQL> select * from t_partition_range;
未选定行
SQL> select * from t_partition_range_tmp;
ID NAME
---------- --------------------
11 a
12 b
13 c
--执行exchange partition,交换数据
SQL> alter table t_partition_range exchange partition p2
2 with table t_partition_range_tmp;
表已更改。
SQL> select * from t_partition_range;
ID NAME
---------- --------------------
11 a
12 b
13 c
SQL> select * from t_partition_range_tmp;
未选定行
--t_partition_range_tmp插入数据,包含t_partition_range 非p2分区的数据
SQL> insert into t_partition_range_tmp values(14,‘d‘);
已创建 1 行。
SQL> insert into t_partition_range_tmp values(9,‘e‘);
已创建 1 行。
SQL> insert into t_partition_range_tmp values(31,‘f‘);
已创建 1 行。
SQL> commit;
提交完成。
SQL> select * from t_partition_range;
ID NAME
---------- --------------------
11 a
12 b
13 c
SQL> select * from t_partition_range_tmp;
ID NAME
---------- --------------------
14 d
9 e
31 f
--再次执行exchange partition
SQL> alter table t_partition_range exchange partition p2
2 with table t_partition_range_tmp;
with table t_partition_range_tmp
*
第 2 行出现错误:
ORA-14099: 未对指定分区限定表中的所有行
--交换失败,因为 t_partition_range_tmp表中值9,31不在 t_partition_range表中p2分区内,附加without validation可交换成功
SQL> alter table t_partition_range exchange partition p2
2 with table t_partition_range_tmp without validation;
表已更改。
SQL> select * from t_partition_range partition(p2);
ID NAME
---------- --------------------
14 d
9 e
31 f
SQL> select * from t_partition_range_tmp;
ID NAME
---------- --------------------
11 a
12 b
13 c
--删除交换后的 t_partition_range表中非p2分区内的值9,31,竟然删除0行。
SQL> delete from t_partition_range where id in(9,31);
已删除0行。
--指定9,31数值查询也查不到
SQL> select * from t_partition_range where id in(9,31);
未选定行
--指定p2分区查询能查出来
SQL> select * from t_partition_range partition(p2);
ID NAME
---------- --------------------
14 d
9 e
31 f
--直接对表做delete操作,记录可全删除
SQL> delete from t_partition_range;
已删除3行。
SQL> rollback;
回退已完成。
SQL> delete from t_partition_range where id=9;
已删除0行。
SQL> delete from t_partition_range where id=14;
已删除 1 行。
SQL> rollback;
回退已完成。
注意:
1、涉及交换的两表之间表结构必须一致,除非附加 with validation 子句 ;
2、如果是从非分区表向分区表做交换,非分区表中的数据必须符合分区表中指定分区的规则,除非 附加 without validation 子句 ,交换后,分区表中指定交换的分区数据含有不符合该分区定义的数据,这些数据无法直接删除或查找(如上例内容所示)。
3、如果从分区表向分区表做交换,被交换的分区的数据必须符合分区规则,除非附加 without validation 子句 ;
Global 索引或涉及到数据改动了的 global 索引分区会被置为 unusable ,除非附加 update indexes 子 句。
提示:
一旦附加了 without validation 子句,则表示不再验证数据有效性,因此指定该子句时务必慎重。
3.6、截断表分区(Truncate Partition)
Truncate partition 就像 truncate table 一样,直接从头部截断数据,用来删除数据那是效率超高无比。 但 是如果该表有外键引用的话,
ddl 的 truncate 就不好使了,只能要么使用delete ,要么先disable掉外键关联再truncate 了。同样,在不指定 update indexes 子句的情况下, truncate partition 也会造成分区所在表的 global索引失效。
语法非常简单: alter table tbname truncate partition/subpartition ptname;
例:
SQL> select * from t_partition_range partition(p2);
ID NAME
---------- --------------------
11 a
12 b
13 c
SQL> alter table t_partition_range truncate partition p2;
表被截断。
SQL> select * from t_partition_range partition(p2);
未选定行
3.7、移动表分区(Move partition)
Move partition 与 modify partition 的功能相似,但又比之更加强劲,比如可以修改分区所在表空间等等 , 与 move table 的操作很类似,某些时间也非常有用,比如降低行迁移。另外,move partition修改分区所在表空间时,分区对应的local索引表空间不会被修改。
语法很简单: Alter table tbname move partition/subpartition ptname .....;
SQL> select partition_name,tablespace_name from user_tab_partitions
2 where table_name=‘T_PARTITION_RANGE‘;
PARTITION_NAME TABLESPACE_NAME
-------------------- --------------------
P1 JJJG
P2 JJJG
P3 JJJG
PMAX JJJG
SQL> alter table t_partition_range move partition p3 tablespace jjjg_data;
表已更改。
SQL> select partition_name,tablespace_name from user_tab_partitions
2 where table_name=‘T_PARTITION_RANGE‘;
PARTITION_NAME TABLESPACE_NAME
-------------------- --------------------
P1 JJJG
P2 JJJG
P3 JJJG_DATA
PMAX JJJG
SQL> edit
已写入 file afiedt.buf
1 select partition_name,tablespace_name from user_ind_partitions
2* where index_name=‘IDX_PARTI_RANGE_ID‘
SQL> /
PARTITION_NAME TABLESPACE_NAME
-------------------- --------------------
P1 JJJG
P2 JJJG
P3 JJJG
PMAX JJJG
提示: move partition/subpartiton 时会锁表,并且 move partition/subpartiton 视被移动分区中数据量的多 少,会带来相应的 IO 操作。同时还需要注意,如果在 move partition/subpartiton 时没有指定 update indexes 子句,则被移动分区所在的 local 索引以及全局索引都会失效,需要手工 rebuilding 。
3.8、重命名表分区(Rename Partition)
就是改名,跟表改名,列改名类似,语法很简单:
alter table tbname rename patition ptname to newptname;
例:
SQL> select partition_name,tablespace_name from user_tab_partitions
2 where table_name=‘T_PARTITION_RANGE‘;
PARTITION_NAME TABLESPACE_NAME
-------------------- --------------------
P1 JJJG
P2 JJJG
P3 JJJG
PMAX JJJG
SQL> alter table t_partition_range rename partition p3 to p4;
表已更改。
SQL> select partition_name,tablespace_name from user_tab_partitions
2 where table_Name=‘T_PARTITION_RANGE‘;
PARTITION_NAME TABLESPACE_NAME
-------------------- --------------------
P1 JJJG
P2 JJJG
P4 JJJG
PMAX JJJG
ORACLE 范围分区 partition-range分区