一、前言
項目中的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:腳本沒有考慮到表分區在不同表空間的情況(業務需求如此),加上也不難,對表空間名字做個類似限制即可,這里不再展開。
