CREATE TRIGGER — 定義一個新觸發器
CREATE [ OR REPLACE ] [ CONSTRAINT ] TRIGGERname
{ BEFORE | AFTER | INSTEAD OF } {event
[ OR ... ] } ONtable_name
[ FROMreferenced_table_name
] [ NOT DEFERRABLE | [ DEFERRABLE ] [ INITIALLY IMMEDIATE | INITIALLY DEFERRED ] ] [ REFERENCING { { OLD | NEW } TABLE [ AS ]transition_relation_name
} [ ... ] ] [ FOR [ EACH ] { ROW | STATEMENT } ] [ WHEN (condition
) ] EXECUTE { FUNCTION | PROCEDURE }function_name
(arguments
) whereevent
can be one of: INSERT UPDATE [ OFcolumn_name
[, ... ] ] DELETE TRUNCATE
CREATE TRIGGER
用於建立一個新的觸發器。 CREATE OR REPLACE TRIGGER
將會建立新觸發器或替換現有觸發器。該觸發器將與指定的表、檢視或外部表關聯,並在對該表執行某些操作時執行指定的函式 function_name
。
要替換現有觸發器的當前定義,請使用 CREATE OR REPLACE TRIGGER
,並指定現有觸發器的名稱和父表。所有其他屬性都將被替換。
觸發器可以指定在操作嘗試作用於一行之前(在約束被檢查以及嘗試 INSERT
、UPDATE
或 DELETE
之前);或者在操作完成之後(在約束被檢查以及 INSERT
、UPDATE
或 DELETE
完成之後);或者代替操作(在對檢視執行插入、更新或刪除時)。如果觸發器在事件之前或代替事件發生,則觸發器可以跳過當前行的操作,或者更改將被插入的行(僅適用於 INSERT
和 UPDATE
操作)。如果觸發器在事件之後發生,則所有更改(包括其他觸發器的效果)對觸發器都是“可見”的。
標記為 FOR EACH ROW
的觸發器對於操作所修改的每一行都會被呼叫一次。例如,一個影響 10 行的 DELETE
會導致目標關係上的任何 ON DELETE
觸發器被呼叫 10 次,每次針對一個被刪除的行。相比之下,標記為 FOR EACH STATEMENT
的觸發器對於任何給定的操作只執行一次,而不管它修改了多少行(特別是,一個修改零行的操作仍然會觸發任何適用的 FOR EACH STATEMENT
觸發器)。
指定 INSTEAD OF
觸發事件的觸發器必須標記為 FOR EACH ROW
,並且只能在檢視上定義。BEFORE
和 AFTER
觸發器在檢視上必須標記為 FOR EACH STATEMENT
。
此外,還可以為 TRUNCATE
定義觸發器,但只能是 FOR EACH STATEMENT
。
下表總結了可用於表、檢視和外部表的觸發器型別
何時 | 事件 | 行級 | 語句級 |
---|---|---|---|
BEFORE |
INSERT /UPDATE /DELETE |
表和外部表 | 表、檢視和外部表 |
TRUNCATE |
— | 表和外部表 | |
AFTER |
INSERT /UPDATE /DELETE |
表和外部表 | 表、檢視和外部表 |
TRUNCATE |
— | 表和外部表 | |
INSTEAD OF |
INSERT /UPDATE /DELETE |
檢視 | — |
TRUNCATE |
— | — |
此外,觸發器定義可以指定一個布林 WHEN
條件,該條件將被測試以確定是否應該觸發觸發器。在行級觸發器中,WHEN
條件可以檢查行的列的舊值和/或新值。語句級觸發器也可以有 WHEN
條件,儘管它們不是很有用,因為條件不能引用表中的任何值。
如果為同一事件定義了多個相同型別的觸發器,它們將按名稱的字母順序觸發。
當指定 CONSTRAINT
選項時,此命令建立一個約束觸發器。這與常規觸發器相同,只是觸發器觸發的時間可以使用 SET CONSTRAINTS
進行調整。約束觸發器必須是普通表(不是外部表)上的 AFTER ROW
觸發器。它們可以在導致觸發事件的語句結束時觸發,或在包含事務結束時觸發;後一種情況稱為延遲。透過使用 SET CONSTRAINTS
,也可以強制立即執行待定的延遲觸發器。約束觸發器應在違反它們實現的約束時引發異常。
REFERENCING
選項支援收集轉換關係,這些是包含當前 SQL 語句插入、刪除或修改的所有行的行集。此功能允許觸發器檢視語句所做操作的全域性檢視,而不僅僅是一次一行。此選項僅允許用於普通表(不是外部表)上的 AFTER
觸發器。觸發器不應該是約束觸發器。此外,如果觸發器是 UPDATE
觸發器,則在使用此選項時不能指定 column_name
列表。OLD TABLE
只能指定一次,並且僅用於可以對 UPDATE
或 DELETE
觸發的觸發器;它建立一個轉換關係,其中包含語句更新或刪除的所有行的前映像。類似地,NEW TABLE
只能指定一次,並且僅用於可以對 UPDATE
或 INSERT
觸發的觸發器;它建立一個轉換關係,其中包含語句更新或插入的所有行的後映像。
SELECT
不會修改任何行,因此您無法建立 SELECT
觸發器。規則和檢視可以為需要 SELECT
觸發器的似是而非的問題提供可行的解決方案。
有關觸發器的更多資訊,請參閱 第 37 章。
name
為新觸發器指定的名稱。該名稱必須與同一表上的任何其他觸發器的名稱不同。該名稱不能包含模式限定符 — 觸發器繼承其表的模式。對於約束觸發器,這也是使用 SET CONSTRAINTS
修改觸發器行為時要使用的名稱。
BEFORE
AFTER
INSTEAD OF
確定是在事件之前、之後還是代替事件呼叫函式。約束觸發器只能指定為 AFTER
。
event
INSERT
、UPDATE
、DELETE
或 TRUNCATE
中的一個;這指定了觸發觸發器的事件。可以使用 OR
指定多個事件,除非請求了轉換關係。
對於 UPDATE
事件,可以使用此語法指定列列表
UPDATE OFcolumn_name1
[,column_name2
... ]
僅當列中至少有一列被指定為 UPDATE
命令的目標,或者列是依賴於目標列的生成列時,觸發器才會觸發。
INSTEAD OF UPDATE
事件不允許列列表。請求轉換關係時也不能指定列列表。
table_name
觸發器所屬的表、檢視或外部表的名稱(可選項,帶模式限定)。
referenced_table_name
由約束引用的另一張表的名稱(可能帶模式限定)。此選項用於外部索引鍵約束,不推薦一般使用。此選項只能為約束觸發器指定。
DEFERRABLE
NOT DEFERRABLE
INITIALLY IMMEDIATE
INITIALLY DEFERRED
觸發器的預設時間。有關這些約束選項的詳細資訊,請參閱 CREATE TABLE 文件。此選項只能為約束觸發器指定。
REFERENCING
此關鍵字緊跟在宣告一個或兩個關係名稱之前,這些關係提供了對觸發語句的轉換關係的訪問。
OLD TABLE
NEW TABLE
此子句指示以下關係名稱是用於前映像轉換關係還是後映像轉換關係。
transition_relation_name
在觸發器內部用於此轉換關係的(未限定的)名稱。
FOR EACH ROW
FOR EACH STATEMENT
這指定是為觸發事件影響的每一行呼叫觸發器函式一次,還是僅為每個 SQL 語句呼叫一次。如果未指定兩者,則預設為 FOR EACH STATEMENT
。約束觸發器只能指定為 FOR EACH ROW
。
condition
一個布林表示式,用於確定觸發器函式是否將實際執行。如果指定了 WHEN
,則僅當 condition
返回 true
時才呼叫函式。在 FOR EACH ROW
觸發器中,WHEN
條件可以透過分別寫 OLD.
或 column_name
NEW.
來引用舊行值和/或新行值。當然,column_name
INSERT
觸發器不能引用 OLD
,DELETE
觸發器不能引用 NEW
。
INSTEAD OF
觸發器不支援 WHEN
條件。
目前,WHEN
表示式不能包含子查詢。
請注意,對於約束觸發器,WHEN
條件的評估不是延遲的,而是在執行行更新操作後立即發生。如果條件不評估為 true,則不會將觸發器排隊以進行延遲執行。
function_name
一個使用者提供的函式,該函式被宣告為不接受任何引數並返回 trigger
型別,當觸發器觸發時執行該函式。
在 CREATE TRIGGER
的語法中,關鍵字 FUNCTION
和 PROCEDURE
是等效的,但引用的函式無論如何都必須是函式,而不是過程。此處使用 PROCEDURE
關鍵字是歷史性的,已棄用。
arguments
一個可選的逗號分隔的引數列表,在觸發器執行時提供給函式。引數是文字字串常量。簡單的名稱和數字常量也可以寫在這裡,但它們都將被轉換為字串。請檢查觸發器函式實現語言的描述,以瞭解這些引數在函式中如何訪問;它可能與常規函式引數不同。
要建立或替換表上的觸發器,使用者必須擁有該表的 TRIGGER
許可權。使用者還必須擁有觸發器函式的 EXECUTE
許可權。
使用 DROP TRIGGER
刪除觸發器。
在分割槽表上建立行級觸發器將導致在其每個現有分割槽上建立一個相同的“克隆”觸發器;之後建立或附加的任何分割槽也將具有相同的觸發器。如果子分割槽上已存在名稱衝突的觸發器,則會發生錯誤,除非使用 CREATE OR REPLACE TRIGGER
,在這種情況下,該觸發器將被替換為克隆觸發器。當分割槽從其父分割槽分離時,其克隆觸發器將被刪除。
列特定的觸發器(使用 UPDATE OF
語法定義的觸發器)將在任何列被列為 column_name
UPDATE
命令的 SET
列表中的目標時觸發。由於 BEFORE UPDATE
觸發器對行內容所做的更改不被考慮,因此即使在觸發器未被觸發的情況下,列的值也可能發生更改。反之,像 UPDATE ... SET x = x ...
這樣的命令將觸發 x
列上的觸發器,即使該列的值沒有改變。
在 BEFORE
觸發器中,WHEN
條件在函式將要或已經執行之前進行評估,因此使用 WHEN
與在觸發器函式開頭測試相同的條件在實質上沒有區別。特別要注意的是,條件看到的 NEW
行是當前值,可能已被之前的觸發器修改。此外,BEFORE
觸發器的 WHEN
條件不允許檢查 NEW
行的系統列(例如 ctid
),因為這些列尚未設定。
在 AFTER
觸發器中,WHEN
條件在行更新發生後立即進行評估,並確定是否將一個事件排隊以在語句結束時觸發觸發器。因此,當 AFTER
觸發器的 WHEN
條件不返回 true 時,無需排隊事件,也無需在語句結束時重新獲取行。如果觸發器只需要為少數幾行觸發,這可以顯著加快修改許多行的語句的速度。
在某些情況下,單個 SQL 命令可能會觸發多種型別的觸發器。例如,帶有 ON CONFLICT DO UPDATE
子句的 INSERT
可能會導致插入和更新操作,因此它會根據需要觸發這兩種型別的觸發器。提供給觸發器的轉換關係特定於其事件型別;因此,INSERT
觸發器將僅看到插入的行,而 UPDATE
觸發器將僅看到更新的行。
由外部索引鍵強制操作(如 ON UPDATE CASCADE
或 ON DELETE SET NULL
)引起的行更新或刪除被視為導致它們的 SQL 命令的一部分(請注意,此類操作永遠不會被延遲)。受影響表上的相關觸發器將被觸發,因此這提供了 SQL 命令觸發與其型別不直接匹配的觸發器的另一種方式。在簡單的情況下,請求轉換關係的觸發器將看到由單個原始 SQL 命令在表中引起的所有更改作為單個轉換關係。但是,在某些情況下,存在請求轉換關係的 AFTER ROW
觸發器會導致由單個 SQL 命令觸發的外部索引鍵強制操作被分成多個步驟,每個步驟都有其自己的轉換關係。在這種情況下,任何存在的語句級觸發器將為每個轉換關係集的建立觸發一次,以確保觸發器在轉換關係中看到每個受影響的行一次且僅一次。
檢視上的語句級觸發器僅在檢視上的操作由行級 INSTEAD OF
觸發器處理時觸發。如果操作由 INSTEAD
規則處理,那麼由規則發出的任何語句都將代替原始的命名檢視的語句執行,因此將觸發的是在替換語句中命名的表上的觸發器。類似地,如果檢視是自動可更新的,那麼該操作將透過自動重寫語句為對檢視基表的動作來處理,因此觸發的是基表的語句級觸發器。
修改分割槽表或具有繼承子表的表會觸發附加到顯式命名錶的語句級觸發器,但不會觸發其分割槽或子表的語句級觸發器。相比之下,行級觸發器會在受影響分割槽或子表的行上觸發,即使它們未在查詢中顯式命名。如果已使用 REFERENCING
子句命名的轉換關係定義了語句級觸發器,那麼所有受影響分割槽或子表的行映像都可以看到。對於繼承子表,行映像僅包括觸發器附加到的表中存在的列。
目前,具有轉換關係的行級觸發器不能在分割槽或繼承子表上定義。此外,分割槽表上的觸發器不能是 INSTEAD OF
。
目前,OR REPLACE
選項不支援約束觸發器。
不建議在事務中替換現有觸發器,該事務已對觸發器表執行了更新操作。已經做出的觸發器觸發決策或觸發決策的一部分將不會被重新考慮,因此結果可能會令人驚訝。
有一些內建觸發器函式可以用於解決常見問題,而無需編寫自己的觸發器程式碼;請參閱 第 9.29 節。
每當 accounts
表中的某一行即將被更新時,執行函式 check_account_update
。
CREATE TRIGGER check_update BEFORE UPDATE ON accounts FOR EACH ROW EXECUTE FUNCTION check_account_update();
修改該觸發器定義,使其僅在 balance
列被指定為 UPDATE
命令的目標時才執行函式。
CREATE OR REPLACE TRIGGER check_update BEFORE UPDATE OF balance ON accounts FOR EACH ROW EXECUTE FUNCTION check_account_update();
此形式僅在 balance
列的值確實發生更改時才執行函式。
CREATE TRIGGER check_update BEFORE UPDATE ON accounts FOR EACH ROW WHEN (OLD.balance IS DISTINCT FROM NEW.balance) EXECUTE FUNCTION check_account_update();
呼叫函式記錄 accounts
的更新,但僅在發生更改時。
CREATE TRIGGER log_update AFTER UPDATE ON accounts FOR EACH ROW WHEN (OLD.* IS DISTINCT FROM NEW.*) EXECUTE FUNCTION log_account_update();
為插入檢視底層表的行,為每一行執行函式 view_insert_row
。
CREATE TRIGGER view_insert INSTEAD OF INSERT ON my_view FOR EACH ROW EXECUTE FUNCTION view_insert_row();
為每個語句執行函式 check_transfer_balances_to_zero
,以確認 transfer
行偏移到淨零。
CREATE TRIGGER transfer_insert AFTER INSERT ON transfer REFERENCING NEW TABLE AS inserted FOR EACH STATEMENT EXECUTE FUNCTION check_transfer_balances_to_zero();
為每一行執行函式 check_matching_pairs
,以確認匹配對同時(透過同一語句)被修改。
CREATE TRIGGER paired_items_update AFTER UPDATE ON paired_items REFERENCING NEW TABLE AS newtab OLD TABLE AS oldtab FOR EACH ROW EXECUTE FUNCTION check_matching_pairs();
第 37.4 節 包含一個用 C 語言編寫的完整觸發器函式示例。
PostgreSQL 中的 CREATE TRIGGER
語句實現了SQL標準的一個子集。以下功能目前缺失
雖然 AFTER
觸發器的轉換表名使用 REFERENCING
子句以標準方式指定,但在 FOR EACH ROW
觸發器中使用的行變數不能在 REFERENCING
子句中指定。它們以一種依賴於觸發器函式所用語言的方式提供,但對於任何一種語言都是固定的。某些語言實際上表現得好像存在一個包含 OLD ROW AS OLD NEW ROW AS NEW
的 REFERENCING
子句。
標準允許將轉換表用於列特定的 UPDATE
觸發器,但然後轉換表中應可見的行集取決於觸發器的列列表。這目前在 PostgreSQL 中尚未實現。
PostgreSQL 僅允許執行使用者定義的函式作為觸發動作。標準允許執行許多其他 SQL 命令,如 CREATE TABLE
,作為觸發動作。這個限制很容易透過建立一個執行所需命令的使用者定義函式來繞過。
SQL 指定多個觸發器應按建立時間順序觸發。PostgreSQL 使用名稱順序,這被認為更方便。
SQL 指定級聯刪除上的 BEFORE DELETE
觸發器在級聯 DELETE
完成之後觸發。PostgreSQL 的行為是 BEFORE DELETE
始終在刪除操作之前觸發,即使是級聯刪除。這被認為更一致。在級聯動作引起的更新期間,BEFORE
觸發器修改行或阻止更新時也存在非標準行為。這可能導致約束衝突或不遵守參照約束的儲存資料。
使用 OR
為單個觸發器指定多個操作是 PostgreSQL 對 SQL 標準的擴充套件。
為 TRUNCATE
觸發觸發器的能力是 PostgreSQL 對 SQL 標準的擴充套件,在檢視上定義語句級觸發器的能力也是如此。
CREATE CONSTRAINT TRIGGER
是 PostgreSQL 對SQL標準的一個擴充套件。 OR REPLACE
選項也是。
如果您在文件中看到任何不正確、不符合您對特定功能的體驗或需要進一步澄清的內容,請使用 此表單 報告文件問題。