1. 程式人生 > >PostgreSQL中使用動態SQL-實現自動按時間建立表分割槽

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  版權宣告:本文為博主原創文章,轉載請附上博文連結!