PostgreSQL中使用動態SQL-實現自動按時間建立表分割槽
PostgreSQL中通過繼承,可以支援基本的表分割槽功能,比如按時間,每月建立一個表分割槽,資料記錄到對應分割槽中。按照官方文件的操作,建立子表和index、修改trigger等工作都必須DBA定期去手動執行,不能實現自動化,非常不方便。
嘗試著通過在plpgsql程式碼中使用動態SQL, 將大表分割槽的運維操作實現自動化, 並且可以重用.
假設某個表 tbl_partition 中有很多記錄, 每一條記錄中採集時間的欄位名為: gather_time, 需要按照這個時間, 每個月的資料自動記錄到一個子表中, 子分割槽表的名稱定義為: tbl_partition_201510之類. 實現方法記錄如下:
1. 建立主表結構, 表名稱 tbl_partition, 其中的時間欄位名: gather_time
CREATE TABLE tbl_partition ( id integer, name text, data numeric, gather_time timestamp );
2. 為主表建立觸發器, 其中,呼叫了觸發器函式 auto_insert_into_tbl_partition('gather_time') CREATE TRIGGER insert_tbl_partition_trigger BEFORE INSERT ON tbl_partition FOR EACH ROW EXECUTE PROCEDURE auto_insert_into_tbl_partition('gather_time'); 注: 雖然觸發器函式預設不帶引數, 此處呼叫仍然必須傳入時間欄位名稱作為引數. 否則, 函式將不知道以何欄位來對主表分割槽!
3. 建立可重用的觸發器函式: auto_insert_into_tbl_partition( time_column_name )
程式碼如下:
CREATE OR REPLACE FUNCTION auto_insert_into_tbl_partition() RETURNS trigger AS $BODY$ DECLARE time_column_name text ; -- 父表中用於分割槽的時間欄位的名稱[必須首先初始化!!] curMM varchar(6); -- 'YYYYMM'字串,用做分割槽子表的字尾 isExist boolean; -- 分割槽子表,是否已存在 startTime text; endTime text; strSQL text; BEGIN -- 呼叫前,必須首先初始化(時間欄位名):time_column_name [直接從呼叫引數中獲取!!] time_column_name := TG_ARGV[0]; -- 判斷對應分割槽表 是否已經存在? EXECUTE 'SELECT $1.'||time_column_name INTO strSQL USING NEW; curMM := to_char( strSQL::timestamp , 'YYYYMM' ); select count(*) INTO isExist from pg_class where relname = (TG_RELNAME||'_'||curMM); -- 若不存在, 則插入前需 先建立子分割槽 IF ( isExist = false ) THEN -- 建立子分割槽表 startTime := curMM||'01 00:00:00.000'; endTime := to_char( startTime::timestamp + interval '1 month', 'YYYY-MM-DD HH24:MI:SS.MS'); strSQL := 'CREATE TABLE IF NOT EXISTS '||TG_RELNAME||'_'||curMM|| ' ( CHECK('||time_column_name||'>='''|| startTime ||''' AND ' ||time_column_name||'< '''|| endTime ||''' ) ) INHERITS ('||TG_RELNAME||') ;' ; EXECUTE strSQL; -- 建立索引 strSQL := 'CREATE INDEX '||TG_RELNAME||'_'||curMM||'_INDEX_'||time_column_name||' ON ' ||TG_RELNAME||'_'||curMM||' ('||time_column_name||');' ; EXECUTE strSQL; END IF; -- 插入資料到子分割槽! strSQL := 'INSERT INTO '||TG_RELNAME||'_'||curMM||' SELECT $1.*' ; EXECUTE strSQL USING NEW; RETURN NULL; END $BODY$ LANGUAGE plpgsql;
說明: (1) 程式碼中使用了 TG_ARGV[0] 來獲取呼叫時傳入的引數: 用於分割槽的時間欄位名.
(2) 程式碼中,通過內建引數 TG_RELNAME 獲得了父表的表名稱.
(3) 首先根據插入時間, 判斷對應分割槽表是否存在? 若存在, 直接插入對應分割槽子表
(4) 若分割槽表還不存在, 先建立分割槽子表和索引, 然後插入資料到所建的子表中.
以上程式碼, 在PostgreSQL v9.4 中除錯通過. 理論上, v8.4以上均支援.
參考:
[1] http://stackoverflow.com/questions/1997337/inserting-new-from-a-generic-trigger-using-execute-in-pl-pgsql
[2] http://blog.csdn.net/neo_liu0000/article/details/6255623
[3] http://www.enterprisedb.com/docs/en/9.4/pg/ddl-partitioning.html --------------------- 作者:zhwzju 來源:CSDN 原文:https://blog.csdn.net/zhwzju/article/details/49636817 版權宣告:本文為博主原創文章,轉載請附上博文連結!