Postgresql
使用目前行和目前表名作為變數的觸發函式
就像我在第一個問題中詳述的那樣,我在Postgres 9.1 DB中有多個佈局相同的表。
它們僅在列值上有所不同:
tbl_log_a tbl_log_b tbl_log_c ...
26 個表(從 a 到 z)。每個表都有一個觸發器,該觸發器呼叫一個名為
trfn_tbl_log_%letter%
(froma
toz
) 的觸發器函式,該函式執行完全相同的操作:CREATE OR REPLACE FUNCTION trfn_tbl_log_a RETURNS trigger AS $BODY$ DECLARE v_timeidx real; BEGIN IF NEW.timetype = 'start' THEN SELECT timeidx FROM tbl_log_a WHERE fnname = NEW.fnname AND timetype = 'start' ORDER BY stmtserial DESC LIMIT 1 INTO v_timeidx; IF FOUND THEN NEW.timeidx := floor(v_timeidx) + 1; ELSE NEW.timeidx := 1; END IF; ELSIF NEW.timetype = 'lap' THEN SELECT timeidx FROM tbl_log_a WHERE fnname = NEW.fnname AND (timetype = 'start' OR timetype = 'lap') ORDER BY stmtserial DESC LIMIT 1 INTO v_timeidx; IF FOUND THEN NEW.timeidx := v_timeidx + 0.001; ELSE RAISE EXCEPTION USING MESSAGE = 'There is not any previous row WHERE fnname = NEW.fnname AND (timetype = start OR timetype = lap)'; END IF; ELSIF NEW.timetype = 'resume' THEN SELECT timeidx FROM tbl_log_a WHERE fnname = NEW.fnname AND (timetype = 'start' OR timetype = 'resume') ORDER BY stmtserial DESC LIMIT 1 INTO v_timeidx; IF FOUND THEN NEW.timeidx := v_timeidx + 0.001; ELSE RAISE EXCEPTION USING MESSAGE = 'There is not any previous row WHERE fnname = NEW.fnname AND timetype = start'; END IF; END IF; return NEW; END $BODY$ LANGUAGE plpgsql;
觸發器定義:
CREATE TRIGGER trfn_tbl_log_a BEFORE INSERT ON tbl_log_a FOR EACH ROW EXECUTE PROCEDURE trfn_tbl_log_a();
所以我必須創建 26 個觸發器函式,每個觸發器函式
tbl_log_%letter%
都完全相同,除了使用的表名(tbl_log_a
在範例中)。有沒有辦法編寫一個通用觸發器函式,可能使用動態 SQL 並參數化表名?
我的觸發器函式使用了幾個表列:
timeidx fnname timetype stmtserial
…還有更多我沒有為尺寸添加的內容,但範例中的所有內容都列出了所有類型。
假設對於同一個觸發器呼叫,您從觸發觸發器的表中的同一行中獲取所有值,您的觸發器函式可能如下所示:
CREATE OR REPLACE FUNCTION trfn_tbl_log_any() RETURNS trigger AS $func$ DECLARE _ct int; BEGIN IF NEW.timetype = 'start' THEN EXECUTE format($$ SELECT floor(t.timeidx) + 1 FROM %s t WHERE t.fnname = $1 AND t.timetype = 'start' ORDER BY t.stmtserial DESC LIMIT 1$$ , TG_RELID::regclass -- concatenate *identifer* .. ) USING NEW.fnname -- .. but pass *value* in USING clause INTO NEW.timeidx; GET DIAGNOSTICS _ct = ROW_COUNT; IF _ct > 0 THEN -- do nothing ELSE NEW.timeidx := 1; END IF; END IF; RETURN NEW; END $func$ LANGUAGE plpgsql;
所有這些都應該在 Postgres 9.1 中工作。但無論如何都要考慮升級到目前版本(目前為 9.4)。
NEW
裡面是不可見的EXECUTE
。使用該USING
子句傳遞新行 (NEW.fnname
) 中的值。使用
TG_RELID
(或TG_TABLE_SCHEMA
和TG_TABLE_NAME
)連接表名,就像我們在您之前的問題中製定的那樣:
NEW
您可以直接從動態查詢中分配行的各個列。用於
GET DIAGNOSTICS _ct = ROW_COUNT;
檢查是否找到行。根據文件:特別注意,
EXECUTE
改變 的輸出GET DIAGNOSTICS
,但不改變FOUND
。順便說一句:動態查詢的邏輯只對
BEFORE
觸發器是正確的。AFTER
觸發器也會看到新插入的行。