postgresql使用觸發器分表

分表的概念和好處請自行查閱,與其他方法相比的優劣也不贅述,這裏只介紹一種分表的方法。

使用觸發器分表:每月一張表,分表表名格式mainNameYYYYMM。

第一步:創建數據庫存儲過程,插入數據時按照規則插入不同分表。插入時如果違反分表的約束會報錯。

CREATE OR REPLACE FUNCTION auto_insert_into_table()
  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 [直接從調用參數中獲取!!]
    -- 沒有顯示的聲明參數,使用TG_ARGV[0]獲取參數
    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  
        -- 創建子分區表,寫明約束。TG_RELNAME爲主表的名字,分表將繼承主表的所有字段,但不會繼承主鍵和索引等,需要手動創建。
        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 := 'ALTER TABLE '||TG_RELNAME||'_'||curMM||' ADD PRIMARY KEY(id) ';
        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;

第二步:創建主表,就是正常的建表語句。如果表已經存在則不用做任何修改。

第三步:爲主表創建觸發器。在每次插入數據時都會執行這個觸發器,最後會執行第一步的存儲過程。將數據插入到對應的分表中。調用觸發器時傳入用於分表的日期字段。

CREATE TRIGGER insert_main_table_trigger BEFORE INSERT ON main_table
FOR EACH ROW
EXECUTE PROCEDURE auto_insert_into_table('date');

經過以上三步,分表操作完成。

最後是一些說明和注意事項:

1、第一步和第三步的作用是在插入數據時判斷date輸入哪個月份,將數據插入到對應分表中。所以主表中沒有數據,數據都在對應子表中。

2、第一步創建分表時,使用語句INHERITS ('||TG_RELNAME||')表明主表和分表是繼承的關係,子表會繼承主表的所有字段定義,所以創建子表時不用定義字段,當主表字段變化時子表會同步變化。但是子表不會繼承父表的主鍵和索引等,需要手動創建。主表的主鍵和索引實質上已經沒有作用,但是還要創建,起到和字段的備註一樣的效果,說明所有子表的結構。

3、因爲是繼承的關係,所以代碼和數據庫語句中只用主表的表名就可以實現對所有子表的增刪改查操作。或者說分表對於使用這張表的人來說是透明的,不用做額外的處理。

4、因爲各分表是相對獨立的,雖然都有唯一主鍵,但都是相對於單個分表。所以當查詢主表時返回的數據中會存在主鍵相同的情況。所以主鍵的生成方式最好用全局唯一uuid。如果只想查主表的數據要用only關鍵字。

5、這裏主要涉及三個知識點:

1)存儲過程:創建分表和插入數據。多個表可以複用用一個存儲過程。

2)觸發器:每次插入數據時觸發對應的存儲過程。每個表要單獨創建觸發器,但可以調用同一個存儲過程。

3)表繼承:1和2是在數據“增”時發揮作用的。在“刪改查”時是表繼承在發揮作用,對主表的所有操作都會直接作用在子表上。創建分表時不要額外創建新的字段。

6、分表和繼承是不同的,分表需要繼承的特性。所以上面看到分表和子表時不要混淆概念。

7、數據庫分表還有其他的方式,這裏不做比較和說明。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章