(一)什么是间隔分区
间隔分区是Oracle 11.1引入的新功能,通过该功能,可以在输入相应分区的数据时自动创建相应的分区。在没有间隔分区技术之前,DBA通常会创建一个maxvalue分区以避免ORA-14400:插入的分区键值不能映射到任何分区("inserted partition key does not map to any partition")。
作为范围分区(range partition)的扩展,间隔分区命令数据库在插入表中的数据超过所有现有范围分区时自动创建指定间隔的分区。DBA必须至少指定一个范围分区的较高值,称为过渡点,数据库会自动的为超出该过渡点的数据创建间隔分区,每个间隔分区的下边界是先前范围或间隔分区的上边界。
(二)创建间隔分区
(2.1)间隔分区创建语法
CREATE TABLE table_name ( ... ) PARTITION BY RANGE(column1) INTERVAL expr [STORE IN (tablespace1,[tablespace2,...])] ( PARTITION partition_name1 VALUES LESS THAN(literal | MAXVALUE) [TABLESPACE tablespace1], PARTITION partition_name2 VALUES LESS THAN(literal | MAXVALUE) [TABLESPACE tablespace2] );
--PARTITION BY RANGE(column1):指定一个分区范围列
--INTERVAL:指定分区间隔
--STORE IN:指定分区存储的表空间
(2.2)间隔分区创建限制
间隔分区是范围分区的扩展,其支持范围有限,在Oracle 11g中,间隔分区只能指定一个分区键列,并且数据类型必须为NUMBER或者DATE类型。因为TIMESTAMP数据类型是DATE类型的扩展,可在分区键中使用。总结来说,间隔分区只支持NUMBER和TIME类型。
(2.3)INTERVAL关键字解读
在创建自动间隔分区的时候,最为核心的就是”INTERVAL“关键字了。对于按照时间进行自动分区,INTERVAL后面可以跟随NUMTOYMINTERVAL和NUMTODSINTERVAL。两个关键字用法如下:
(2.3.1)NUMTOYMINTERVAL(x,c)
用法:x是一个数据,c是一个字符串,该函数是将x转为interval year to month类型。常用单位有:”year“、”month“。
例子:当前时间加3年,当前时间加3个月
SELECT SYSDATE, SYSDATE + NUMTOYMINTERVAL(3,'year') AS "3年后" , SYSDATE + NUMTOYMINTERVAL(3,'month') AS "3个月后" FROM dual;
(2.3.2)NUMTODSINTERVAL(x,c)
用法:x是一个数据,c是一个字符串,该函数是将x转为interval day to second类型。常用单位有:”day“、”hour“、”minute“、”second“。
例子:当前时间加1天、1小时、1分钟、1秒钟。
(三)按时间(年、月、日、周)创建间隔分区
(3.1)按”年“自动创建分区(关键字:NUMTOYMINTERVAL)
例子:创建按年自动分区表,按照员工生日(birthday字段),每年一个分区。
--创建按年分区表 CREATE TABLE interval_year_table01 ( employee_id NUMBER, employee_name VARCHAR2(20), birthday DATE ) PARTITION BY RANGE(birthday) INTERVAL (NUMTOYMINTERVAL(1,'year')) STORE IN (tbs01,tbs02,tbs03) ( PARTITION partition2014 VALUES LESS THAN(to_date('2015-01-01:00:00:00','yyyy-mm-dd hh24:mi:ss')), PARTITION partition2015 VALUES LESS THAN(to_date('2016-01-01:00:00:00','yyyy-mm-dd hh24:mi:ss')) );
(3.2)按”月“自动创建分区(关键字:NUMTOYMINTERVAL)
例子:创建按月自动分区表,按照员工生日(birthday字段),每月一个分区。
-- 创建按月分区表 CREATE TABLE interval_month_table01 ( employee_id NUMBER, employee_name VARCHAR2(20), birthday DATE ) PARTITION BY RANGE(birthday) INTERVAL (NUMTOYMINTERVAL(1,'month')) STORE IN (tbs01,tbs02,tbs03) ( PARTITION partition201401 VALUES LESS THAN(to_date('2014-02-01:00:00:00','yyyy-mm-dd hh24:mi:ss')) );
(3.3)按”天(日)“自动创建分区(关键字:NUMTODSINTERVAL)
例子:创建按天自动分区表,按照员工生日(birthday字段),每天一个分区。
-- 按天(日)创建分区 CREATE TABLE interval_day_table01 ( employee_id NUMBER, employee_name VARCHAR2(20), birthday DATE ) PARTITION BY RANGE(birthday) INTERVAL (NUMTODSINTERVAL(1,'day')) STORE IN (tbs01,tbs02,tbs03) ( PARTITION partition20140101 VALUES LESS THAN(to_date('2014-01-01 00:00:00','yyyy-mm-dd hh24:mi:ss')) );
(3.4)按”周“自动创建分区(关键字:NUMTODSINTERVAL)
例子:创建按周自动分区表,按照员工生日(birthday字段),每周一个分区。需要注意的是,这里使用的关键字与“天”分区一样,都是“day”,只是改为了7天。
-- 按周创建分区 CREATE TABLE interval_week_table01 ( employee_id NUMBER, employee_name VARCHAR2(20), birthday DATE ) PARTITION BY RANGE(birthday) INTERVAL (NUMTODSINTERVAL(7,'day')) STORE IN (tbs01,tbs02,tbs03) ( PARTITION partition201401w VALUES LESS THAN(to_date('2014-01-07 00:00:00','yyyy-mm-dd hh24:mi:ss')) );
(3.4)按”小时“自动创建分区(关键字:NUMTODSINTERVAL)
-- 按”小时”进行分区
CREATE TABLE interval_hour_table01 ( employee_id NUMBER, employee_name VARCHAR2(20), birthday DATE ) PARTITION BY RANGE(birthday) INTERVAL (NUMTODSINTERVAL(1,'hour')) STORE IN (tbs01,tbs02,tbs03) ( PARTITION partition20140100 VALUES LESS THAN(to_date('2014-01-01 01:00:00','yyyy-mm-dd hh24:mi:ss')) );
除此之外,还可以按”分钟“、”秒“进行自动分区。
(四)按数字(number)创建间隔分区
例子:按数字进行分区相对简单,这里相近的10个数字进入同一个分区
CREATE TABLE interval_number_table01 ( employee_id NUMBER, employee_name VARCHAR2(20), birthday DATE ) PARTITION BY RANGE(employee_id) INTERVAL (10) STORE IN (tbs01,tbs02,tbs03) ( PARTITION partition10 VALUES LESS THAN(10) );
(五)关于间隔分区的常见问题
(5.1)如何将现有普通表转换为间隔分区
可以使用如下命令将现有的范围分区表转换为间隔分区表,注意,仅仅支持范围分区表:
ALTER TABLE <table_name> SET INTERVAL <number or interval expression>;
(5.2)如何为现有表设置新的间隔
可以使用如下命令修改间隔,该操作不会使index不可用:
ALTER TABLE <table_name> SET INTERVAL(interval express);
例子见MOS文档(1479115.1)。
(5.3)如何为间隔分区指定/更改表空间
INTERVAL子句的STORE IN用于指定创建间隔分区的表空间。如果指定了表空间列表,将以循环方式在这些表空间上创建间隔分区。
INTERVAL expr [STORE IN (tablespace1,[tablespace2,...])]
需要注意的是,在INTERVAL子句中使用”PARTITION“创建的范围分区需要指出表空间,否则会将范围分区创建到用户默认的表空间中,而不是[STORE IN]的表空间中。
对于已经创建的分区,可以使用以下命令将其移动到特定的表空间:
--移动分区到特定表空间 ALTER TABLE <table_name> MOVE PARTITION <partition_name> TABLESPACE <tablespace_name>; --注:移动分区会导致全局索引失效,需谨慎
(5.4)自动创建的间隔分区的名称是什么
数据库创建的间隔分区的名称是系统自动生成的,可以通过dba_tab_partition视图查看。目前无法为分区指定创建模板,但是可以重命名分区。
例子:自动创建的表空间的名称
insert into INTERVAL_NUMBER_TABLE01 values (201209, 'name09'); insert into INTERVAL_NUMBER_TABLE01 values (201210, 'name10'); insert into INTERVAL_NUMBER_TABLE01 values (201211, 'name11'); insert into INTERVAL_NUMBER_TABLE01 values (201212, 'name12'); insert into INTERVAL_NUMBER_TABLE01 values (201301, 'name01'); insert into INTERVAL_NUMBER_TABLE01 values (201402, 'name02'); insert into INTERVAL_NUMBER_TABLE01 values (201503, 'name03'); SQL> select table_owner,table_name,partition_name,high_value,tablespace_name,interval 2 from dba_tab_partitions 3 where table_name = 'INTERVAL_NUMBER_TABLE01'; TABLE_OWNER TABLE_NAME PARTITION_NAME HIGH_VALUE TABLESPACE_NAME INTERVAL -------------- ------------------------------ ----------------- ------------ ----------------- -------- LIJIAMAN INTERVAL_NUMBER_TABLE01 PARTITION10 10 USERS NO LIJIAMAN INTERVAL_NUMBER_TABLE01 SYS_P54 20 TBS02 YES LIJIAMAN INTERVAL_NUMBER_TABLE01 SYS_P55 110 TBS02 YES LIJIAMAN INTERVAL_NUMBER_TABLE01 SYS_P56 120 TBS03 YES LIJIAMAN INTERVAL_NUMBER_TABLE01 SYS_P57 130 TBS01 YES --备注:INTERVAL = 'YES'代表自动创建的分区
(5.5)使用DBMS_METADATA.GET_DDL检索表时,为什么缺少系统生成的间隔分区?
"DBMS_METADATA.GET_DDL"只提供用户手段创建的分区,而不提供系统自动生成的分区。以下为测试例子:
SQL> SELECT DBMS_METADATA.GET_DDL('TABLE','INTERVAL_NUMBER_TABLE01','LIJIAMAN') FROM DUAL;
DBMS_METADATA.GET_DDL('TABLE', -------------------------------------------------------------------------------- CREATE TABLE "LIJIAMAN"."INTERVAL_NUMBER_TABLE01" ( "EMPLOYEE_ID" NUMBER, "EMPLOYEE_NAME" VARCHAR2(20), "BIRTHDAY" DATE ) PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 STORAGE( BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT) TABLESPACE "USERS" PARTITION BY RANGE ("EMPLOYEE_ID") INTERVAL (10) STORE IN ("TBS01", "TBS02", "TBS03") (PARTITION "PARTITION10" VALUES LESS THAN (10) SEGMENT CREATION IMMEDIATE PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 NOCOMPRESS LOGGING STORAGE(INITIAL 8388608 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645 PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT) TABLESPACE "USERS" )
如果要输出系统自动创建的分区的脚本,需将DBMS_METDATA的EXPORT参数设置为true
exec dbms_metadata.set_transform_param(dbms_metadata.SESSION_TRANSFORM,'EXPORT',true);
(六)关于间隔分区的bug
以前在使用分区表时,遇到过由延迟段创建(deferred_segment_creation)引起的段分配异常问题。间隔分区也有类似的bug,使用需要谨慎。
Bug 16042673 - Database hang when system trying to add interval partition to the table (Doc ID 16042673.8)
Symptoms:
Related To:
- Hang (Process Hang)
- Waits for "library cache lock"
- Waits for "library cache load lock"
Description
This bug is only relevant when using Partitioned TablesConcurrent insert statements issued against an interval partitioned table resulting in the creation of a new partition blocks remaining inserts into the same table. Rediscovery Notes If concurrent insert statements are issued against an interval partitioned table and the insert statement which results in the creation of a new partition blocks all the other inserts into the table, then we might be encountering this bug.
(七)间隔分区的利弊思考
好处:间隔分区通过系统自动创建分区,减少了DBA的日常运维工作,避免了ORA-14400这类错误,每年年终不需要为下一年手动创建分区,想想还是挺开心的;
坏处:因为系统自动创建分区名称,我们无法通过分区名称来判断数据的存放位置,增加了后期的维护难度。举个例子,如果是DBA手动维护,假设表的分区”part_201901“存储的就是2019年1月的数据,假如我们想要删除1月份的数据,直接删除该分区即可,如果数据库里面有500个类似的表,直接写批量脚本”ALTER TABLE <table_name> DROP PARTITION part_201901“就将全部表的1月份的数据删除了,但是对于系统自动创建的分区,在不同的表里面,2019年1月的数据对应的分区名不同,自然无法使用脚本批量删除,即使有脚本,也非常麻烦。
-------------------------------------------------------------------------------------------------------
分区(partition)技术相关文档:
1. [oracle]分区表学习
2. [Oracle]分区索引
3. Oracle分区表删除分区引发错误ORA-01502: 索引或这类索引的分区处于不可用状态
4. Oracle split分区表引起ORA-01502错误
5. Oracle在线重定义(online redefinition)--将普通表改为分区表
-------------------------------------------------------------------------------------------------------