支援的版本:目前 (17) / 16 / 15 / 14 / 13
開發版本:devel
不支援的版本:12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3 / 8.2 / 8.1 / 8.0 / 7.4 / 7.3 / 7.2 / 7.1

CREATE TRIGGER

CREATE TRIGGER — 定義一個新的觸發器

概要

CREATE [ OR REPLACE ] [ CONSTRAINT ] TRIGGER name { BEFORE | AFTER | INSTEAD OF } { event [ OR ... ] }
    ON table_name
    [ FROM referenced_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 )

where event can be one of:

    INSERT
    UPDATE [ OF column_name [, ... ] ]
    DELETE
    TRUNCATE

描述

CREATE TRIGGER 建立一個新的觸發器。CREATE OR REPLACE TRIGGER 將建立一個新的觸發器,或替換現有的觸發器。觸發器將與指定的資料表、檢視表或外部資料表相關聯,並且在該資料表上執行特定操作時,將執行指定的函數 function_name

若要替換現有觸發器的目前定義,請使用 CREATE OR REPLACE TRIGGER,指定現有觸發器的名稱和父資料表。所有其他屬性都會被替換。

可以指定觸發器在對資料列嘗試操作之前觸發(在檢查約束之前以及嘗試 INSERTUPDATEDELETE 之前);或在操作完成之後觸發(在檢查約束之後以及 INSERTUPDATEDELETE 完成之後);或取代該操作(針對檢視表上的插入、更新或刪除)。如果觸發器在事件之前或取代事件觸發,則觸發器可以跳過目前資料列的操作,或變更正在插入的資料列(僅適用於 INSERTUPDATE 操作)。如果觸發器在事件之後觸發,則所有變更,包括其他觸發器的效果,對觸發器都是可見的

標記為 FOR EACH ROW 的觸發器,對於操作修改的每個資料列都會呼叫一次。例如,影響 10 個資料列的 DELETE 將導致目標關聯上的任何 ON DELETE 觸發器被呼叫 10 次,每個已刪除的資料列一次。相反地,標記為 FOR EACH STATEMENT 的觸發器,對於任何給定的操作只執行一次,無論它修改多少個資料列(特別是,修改零個資料列的操作仍將導致執行任何適用的 FOR EACH STATEMENT 觸發器)。

指定為觸發事件 INSTEAD OF 觸發的觸發器,必須標記為 FOR EACH ROW,並且只能在檢視表上定義。檢視表上的 BEFOREAFTER 觸發器必須標記為 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 只能指定一次,且僅適用於可以觸發 UPDATEDELETE 的觸發程序;它會建立一個轉變關係,包含陳述式更新或刪除的所有列的前映像。同樣地,NEW TABLE 只能指定一次,且僅適用於可以觸發 UPDATEINSERT 的觸發程序;它會建立一個轉變關係,包含陳述式更新或插入的所有列的後映像

SELECT 不會修改任何列,因此您無法建立 SELECT 觸發程序。規則和檢視表可能會為看似需要 SELECT 觸發程序的問題提供可行的解決方案。

有關觸發程序的更多資訊,請參閱第 37 章

參數

name

賦予新觸發程序的名稱。它必須與同一表格的其他任何觸發程序的名稱不同。名稱不能具有綱要限定詞 — 觸發程序會繼承其表格的綱要。對於約束觸發程序,這也是使用 SET CONSTRAINTS 修改觸發程序行為時要使用的名稱。

BEFORE
AFTER
INSTEAD OF

決定是在事件之前、之後還是代替事件呼叫函式。約束觸發程序只能指定為 AFTER

event

其中之一:INSERTUPDATEDELETETRUNCATE;這指定將觸發觸發程序的事件。可以使用 OR 指定多個事件,除非要求轉變關係。

對於 UPDATE 事件,可以使用以下語法指定欄位清單

UPDATE OF column_name1 [, column_name2 ... ]

只有當至少一個列出的欄位被提及為 UPDATE 命令的目標,或者如果列出的欄位之一是依賴於作為 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_nameNEW.column_name 分別參照舊的和/或新的列值的欄位。當然,INSERT 觸發程序不能參照 OLD,而 DELETE 觸發程序不能參照 NEW

INSTEAD OF 觸發程序不支援 WHEN 條件。

目前,WHEN 運算式不能包含子查詢。

請注意,對於約束觸發程序,WHEN 條件的評估不會延遲,而是在執行列更新操作後立即發生。如果條件的評估結果不是 true,則觸發程序不會排隊等待延遲執行。

function_name

使用者提供的函式,宣告為不帶引數且傳回類型 trigger,並在觸發程序觸發時執行。

CREATE TRIGGER 的語法中,關鍵字 FUNCTIONPROCEDURE 是等效的,但所參照的函式在任何情況下都必須是函式,而不是程序。此處使用關鍵字 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 CASCADEON 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 NEWREFERENCING 子句。

  • 標準允許轉換表與特定欄位的 UPDATE 觸發程序一起使用,但應該在轉換表中可見的列集合取決於觸發程序的欄位列表。 PostgreSQL 目前尚未實作此功能。

  • PostgreSQL 僅允許執行使用者定義的函數作為觸發操作。標準允許執行許多其他的 SQL 指令,例如 CREATE TABLE,作為觸發操作。這個限制並不難繞過,可以建立一個使用者定義的函數來執行所需的指令。

SQL 規範中,多個觸發程序應按照建立時間的順序觸發。PostgreSQL 則使用名稱順序,認為這樣更方便。

SQL 規範指定,級聯刪除上的 BEFORE DELETE 觸發程序應在級聯 DELETE 完成 之後 觸發。PostgreSQL 的行為則是 BEFORE DELETE 總是會在刪除動作之前觸發,即使是級聯刪除也是如此。這被認為更一致。如果在由參考動作引起的更新期間,BEFORE 觸發程序修改列或阻止更新,也存在非標準的行為。這可能導致約束違規或儲存的資料不遵守參考約束。

使用 OR 為單個觸發程序指定多個操作的能力是 PostgreSQL 對 SQL 標準的擴展。

TRUNCATE 觸發觸發程序的能力是 PostgreSQL 對 SQL 標準的擴展,在視圖上定義語句級觸發程序也是如此。

CREATE CONSTRAINT TRIGGERPostgreSQLSQL標準的擴展。 OR REPLACE 選項也是如此。

提交更正

如果您在文件中看到任何不正確、與您使用特定功能的經驗不符或需要進一步澄清的地方,請使用 此表單 回報文件問題。