一、前言
项目中的Oracle库中有一张表(假设表名为Part_Tab),其中一列为Part_ID(int型),计划以该列作为分区依据。
通常Part_ID的值有较固定区间(假设为1~10),但特殊情况下可能超出此范围;此外,其值并不连续。
根据业务,要求以存储过程实现按照Part_ID增、删分区的功能。
二、设计
1 根据需求,不宜采用list型分区,万一删除某个Part_ID后忘记了,又按这个Part_ID插入数据,就会产生运行错误。最终采用传统的range型方式。
2 删除分区没有特别的难度,但在增加分区时,要考虑到如果Part_ID的值如果不比现有分区的range值都大,则不能简单地add分区,而要把某个现有分区进行split。
3 确定split哪个具体分区时,依据是user_tab_partition视图的high_value字段,但很不幸该字段为long类型,无法直接用在where条件中(报Ora-00997错误),虽然可转换成number型,但过程却十分麻烦(不能直接使用to_number,报Ora-00932错误)。
4 为此进行一点变通,将该表的分区名设定为:“表名_“||指定位数的PART_ID(这应该也是许许多多表分区名的传统方法),再以user_tab_partition视图的parttion_name进行判断就方便多了。
三、建表脚本
--创建表按较固定的范围创建初始分区
create table PART_TAB ( PART_ID int not null, ... primary key (PART_ID, ...) ) PARTITION BY RANGE(PART_ID) ( PARTITION PART_TAB_01 VALUES LESS THAN (2) TABLESPACE ..., PARTITION PART_TAB_02 VALUES LESS THAN (3) TABLESPACE ..., PARTITION PART_TAB_03 VALUES LESS THAN (4) TABLESPACE ..., PARTITION PART_TAB_04 VALUES LESS THAN (5) TABLESPACE ..., PARTITION PART_TAB_05 VALUES LESS THAN (6) TABLESPACE ..., PARTITION PART_TAB_06 VALUES LESS THAN (7) TABLESPACE ..., PARTITION PART_TAB_07 VALUES LESS THAN (8) TABLESPACE ..., PARTITION PART_TAB_08 VALUES LESS THAN (9) TABLESPACE ..., PARTITION PART_TAB_09 VALUES LESS THAN (10) TABLESPACE ..., PARTITION PART_TAB_10 VALUES LESS THAN (11) TABLESPACE ..., );
四、删除分区脚本
--存储过程:删除指定分区及数据 --如果该分区不存在则直接退出 create or replace procedure DROP_PART(pPartID in number) IS cnt int; vPartName varchar2(20); BEGIN vPartName := 'PART_TAB_' || trim(to_char(pPartID, '09')); select count(*) into cnt from USER_TAB_PARTITIONS where table_name = 'PART_TAB' and partition_name = vPartName; if cnt >= 1 then execute immediate 'alter table PART_TAB DROP PARTITION ' || vPartName; end if; END;
五、增加分区脚本
--存储过程:增加指定分区 --如果该分区已存在则直接退出 create or replace procedure ADD_PART(pPartID in number) IS cnt int; vNewPartName varchar2(20); vOldPartName varchar2(20); BEGIN vNewPartName := 'PART_TAB_' || trim(to_char(pPartID, '09')); select count(*) into cnt from USER_TAB_PARTITIONS where table_name = 'PART_TAB' and partition_name = vNewPartName; if cnt < 1 then --如果指定分区不存在,则继续 select min(partition_name) into vOldPartName from USER_TAB_PARTITIONS where table_name = 'PART_TAB' and partition_name > vNewPartName; if vOldPartName is null then --仅当新分区的Range值比全部已有分区的Range值时,直接增加分区 execute immediate 'alter table PART_TAB ADD PARTITION ' || vNewPartName || ' values less than (' || to_char(pPartID + 1) || ')'; else --否则,要把最接近的分区按Range值进行split execute immediate 'alter table PART_TAB SPLIT PARTITION ' || vOldPartName || ' AT (' || to_char(pPartID + 1) || ') into (PARTITION ' || vNewPartName || ', PARTITION ' || vOldPartName || ')'; end if; end if; END;
PS:脚本没有考虑到表分区在不同表空间的情况(业务需求如此),加上也不难,对表空间名字做个类似限制即可,这里不再展开。