W3Cschool
恭喜您成為首批注冊(cè)用戶
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
一個(gè)觸發(fā)器聲明了當(dāng)執(zhí)行一種特定類型的操作時(shí)數(shù)據(jù)庫(kù)應(yīng)該自動(dòng)執(zhí)行一個(gè)特殊的函數(shù)。觸發(fā)器可以被附加到表(分區(qū)的或者不分區(qū)的)、視圖和外部表。
在表和外部表上,觸發(fā)器可以被定義為在 INSERT
、UPDATE
或 DELETE
操作之前或之后被執(zhí)行, 可以為每個(gè)SQL語(yǔ)句被執(zhí)行一次或者為每個(gè)修改的行 被執(zhí)行一次。UPDATE
觸發(fā)器可以進(jìn)一步地設(shè)置為只針對(duì)
UPDATE
語(yǔ)句的SET
子句的特定列出發(fā)。觸發(fā)器也可以被 TRUNCATE
語(yǔ)句觸發(fā)。如果一個(gè)觸發(fā)器事件發(fā)生, 觸發(fā)器函數(shù)會(huì)在適當(dāng)?shù)氖录徽{(diào)用來(lái)處理該事件。
在視圖上,觸發(fā)器可以被定義來(lái)取代INSERT
、UPDATE
或 DELETE
操作的執(zhí)行。這種INSTEAD OF
觸發(fā)器對(duì)視圖中需要被修改的每一行觸發(fā)一次。觸發(fā)器函數(shù)的職責(zé)是對(duì)視圖的底層基本表執(zhí)行必要的修改,并且在合適的時(shí)候返回被修改的行以便顯示在視圖中。視圖上的觸發(fā)器也可以被定義為對(duì)每個(gè)
SQL語(yǔ)句執(zhí)行一次,在INSERT
\UPDATE
或DELETE
操作之前或之后。不過(guò),只有在該視圖上還有一個(gè)INSTEAD OF
觸發(fā)器時(shí),上述那些觸發(fā)器才會(huì)被觸發(fā)。否則,以該視圖為目標(biāo)的任何語(yǔ)句都必須被重寫成一個(gè)影響其底層基表的語(yǔ)句,然后附著在那些基表上的觸發(fā)器將會(huì)被引發(fā)。
觸發(fā)器函數(shù)必須在觸發(fā)器本身被創(chuàng)建之前被定義好。觸發(fā)器函數(shù)必須被定義成一個(gè)沒(méi)有參數(shù)的函數(shù),并且返回類型為trigger
(觸發(fā)器函數(shù)通過(guò)一個(gè)特殊傳遞的TriggerData
結(jié)構(gòu)作為其輸入,而不是以普通函數(shù)參數(shù)的形式)。
一旦一個(gè)合適的觸發(fā)器函數(shù)被創(chuàng)建,就可以使用CREATE TRIGGER建立觸發(fā)器。同一個(gè)觸發(fā)器函數(shù)可以被用于多個(gè)觸發(fā)器。
PostgreSQL同時(shí)提供每行的觸發(fā)器和每語(yǔ)句的觸發(fā)器。對(duì)于一個(gè)每行的觸發(fā)器,對(duì)于觸發(fā)觸發(fā)器的語(yǔ)句所修改的每一行都會(huì)調(diào)用一次觸發(fā)器函數(shù)。相反,一個(gè)每語(yǔ)句的觸發(fā)器對(duì)于其觸發(fā)語(yǔ)句只被調(diào)用一次,而不管該語(yǔ)句影響了多少行。特別地,一個(gè)不影響任何行的語(yǔ)句仍然會(huì)導(dǎo)致任何可用每語(yǔ)句的觸發(fā)器的執(zhí)行。這兩類觸發(fā)器有時(shí)也分別被稱作行級(jí)觸發(fā)器和
語(yǔ)句級(jí)觸發(fā)器。TRUNCATE
上的觸發(fā)器只能被定義在語(yǔ)句級(jí)。
觸發(fā)器也可以根據(jù)它們是否在操作之前、之后觸發(fā),或者被觸發(fā)來(lái)取代操作來(lái)分類。它們分別指BEFORE
觸發(fā)器、AFTER
觸發(fā)器以及INSTEAD OF
觸發(fā)器。語(yǔ)句級(jí)
BEFORE
觸發(fā)器在語(yǔ)句開(kāi)始做任何事情之前被觸發(fā),而語(yǔ)句級(jí)AFTER
觸發(fā)器則在語(yǔ)句做完所有事情之后被觸發(fā)。這些觸發(fā)器類型可以被定義在表、視圖或外部表上。行級(jí)BEFORE
觸發(fā)器在每一個(gè)行被操作之前被觸發(fā),而行級(jí)AFTER
觸發(fā)器在語(yǔ)句結(jié)束之后被觸發(fā)(但在任何語(yǔ)句級(jí)AFTER
觸發(fā)器之前)。這些觸發(fā)器類型只能被定義在表和外部表上,但不能定義在視圖上。
INSTEAD OF
觸發(fā)器只能被定義在視圖上,并且只能定義在行級(jí),當(dāng)視圖中的每一行被標(biāo)識(shí)為需要被操作時(shí),它們會(huì)立即觸發(fā)。
一個(gè)以繼承或者分區(qū)層次中父表為目標(biāo)的語(yǔ)句不會(huì)導(dǎo)致受影響的子表的語(yǔ)句級(jí)觸發(fā)器被引發(fā),只有父表的語(yǔ)句級(jí)觸發(fā)器會(huì)被引發(fā)。不過(guò),受影響的子表的行級(jí)觸發(fā)器將被引發(fā)。
如果一個(gè)INSERT
包含ON CONFLICT
DO UPDATE
子句并且引用了EXCLUDED
列,有可能所有行級(jí) BEFORE
INSERT
觸發(fā)器和所有行級(jí) BEFORE
UPDATE
觸發(fā)器的效果可能會(huì)以一種對(duì)于 被更新行最終狀態(tài)透明的方式被應(yīng)用。不過(guò),對(duì)于要執(zhí)行的兩種集合的行級(jí)
BEFORE
觸發(fā)器都不需要有EXCLUDED
列引用。當(dāng)同時(shí)有行級(jí) BEFORE
INSERT
和 BEFORE
UPDATE
觸發(fā)器影響被插入/ 更新的行時(shí)(如果在兩者不冪等時(shí)修改或多或少地等價(jià),這仍可能是有問(wèn)題的),
應(yīng)該考慮可能出現(xiàn)的意料之外的結(jié)果。注意在指定了 ON CONFLICT DO UPDATE
時(shí),不管有沒(méi)有行被 UPDATE
影響(并且不管是否采用了其他 UPDATE
路徑),語(yǔ)句級(jí) UPDATE
都將被執(zhí)行。一個(gè)帶有 ON CONFLICT DO UPDATE
子句的
INSERT
將首先執(zhí)行語(yǔ)句級(jí)BEFORE
INSERT
, 然后執(zhí)行語(yǔ)句級(jí)BEFORE
UPDATE
觸發(fā)器, 接著是語(yǔ)句級(jí)AFTER
UPDATE
觸發(fā)器,
最后是語(yǔ)句級(jí)AFTER
INSERT
觸發(fā)器。
如果一個(gè)分區(qū)表上的UPDATE
導(dǎo)致一行移動(dòng)到另一個(gè)分區(qū),它將被從原始分區(qū)DELETE
掉然后再INSERT
到新分區(qū)中。在這種情況下,原始分區(qū)上所有的行級(jí)BEFORE
UPDATE
觸發(fā)器和所有行級(jí)BEFORE
DELETE
觸發(fā)器會(huì)被引發(fā)。然后目標(biāo)分區(qū)上所有的行級(jí)BEFORE
INSERT
觸發(fā)器會(huì)被引發(fā)。當(dāng)所有這些觸發(fā)器都影響被移動(dòng)的行時(shí),應(yīng)該對(duì)令人驚訝的結(jié)果有心理準(zhǔn)備。至于AFTER ROW
觸發(fā)器,AFTER
DELETE
和
AFTER
INSERT
觸發(fā)器會(huì)被應(yīng)用,但AFTER
UPDATE
觸發(fā)器不會(huì)被應(yīng)用,因?yàn)?code class="command">UPDATE已經(jīng)被轉(zhuǎn)換成了一個(gè)DELETE
和一個(gè)INSERT
。對(duì)于語(yǔ)句級(jí)觸發(fā)器,即便發(fā)生行移動(dòng),
DELETE
和INSERT
觸發(fā)器也都不會(huì)被引發(fā),只有UPDATE
語(yǔ)句中用到的目標(biāo)表上的UPDATE
觸發(fā)器將被引發(fā)。
被語(yǔ)句級(jí)觸發(fā)器調(diào)用的觸發(fā)器函數(shù)應(yīng)該總是返回NULL
。根據(jù)行級(jí)觸發(fā)器的選擇,被其調(diào)用的觸發(fā)器函數(shù)可以返回一個(gè)表行(類型HeapTuple
的一個(gè)值)給執(zhí)行器。在一個(gè)操作前觸發(fā)的行級(jí)觸發(fā)器有下列選擇:
它可以返回NULL
來(lái)跳過(guò)對(duì)當(dāng)前行的操作。這指示執(zhí)行器不要執(zhí)行調(diào)用觸發(fā)器的行級(jí)操作(對(duì)一個(gè)特定表行的插入、修改或刪除)。
僅對(duì)行級(jí)INSERT
和UPDATE
觸發(fā)器來(lái)說(shuō),被返回的行稱為將要被插入的行或者替代將被更新的行。這允許觸發(fā)器函數(shù)修改將要被插入或更新的行。
一個(gè)無(wú)意導(dǎo)致任何這些行為的行級(jí)BEFORE
觸發(fā)器必須小心地它的結(jié)果,使之和被傳入的行一樣(即,INSERT
和UPDATE
觸發(fā)器的NEW
行,DELETE
觸發(fā)器的OLD
行)。
一個(gè)行級(jí)INSTEAD OF
觸發(fā)器可以返回NULL
來(lái)指示它沒(méi)有修改任何來(lái)自于視圖底層基表的數(shù)據(jù),也可以返回被傳入的視圖行(INSERT
和UPDATE
操作的NEW
行,或者DELETE
操作的
OLD
行)。一個(gè)非空返回值被用于標(biāo)志觸發(fā)器在視圖中執(zhí)行了必須的數(shù)據(jù)修改。這將會(huì)導(dǎo)致被命令修改的行計(jì)數(shù)被增加。僅對(duì)于INSERT
和UPDATE
操作,觸發(fā)器可能會(huì)在返回NEW
行之前對(duì)其進(jìn)行修改。這將會(huì)改變INSERT RETURNING
或UPDATE RETURNING
返回的數(shù)據(jù),并在視圖無(wú)法正確地顯示提供給它的相同數(shù)據(jù)時(shí)有用。
對(duì)于在一個(gè)操作之后觸發(fā)的行級(jí)觸發(fā)器,返回值會(huì)被忽略,因此它們可以返回NULL
。
一些情況適用于生成的列。
存儲(chǔ)生成的列在BEFORE
觸發(fā)器之后和 AFTER
觸發(fā)器之前計(jì)算. 因此,生成的值可以在AFTER
觸發(fā)器中檢查。 在BEFORE
觸發(fā)器中,OLD
行包含舊的生成的值,正如人們所期待的,但 NEW
行尚未包含新的生成值并且不應(yīng)訪問(wèn)。
在C語(yǔ)言界面中,此時(shí)列的內(nèi)容還沒(méi)有被定義;在BEFORE
觸發(fā)器中,高級(jí)別編程語(yǔ)言應(yīng)阻止訪問(wèn)NEW
行中存儲(chǔ)生成的列,。 在BEFORE
觸發(fā)器中更改到生成列的值將被忽略并覆蓋。
如果為同一個(gè)關(guān)系上的同一事件定義了超過(guò)一個(gè)觸發(fā)器,它們將按照其名稱的字母表順序被觸發(fā)。在BEFORE
和INSTEAD OF
觸發(fā)器的情況下,每一個(gè)觸發(fā)器返回的可能被修改的行將成為下一個(gè)觸發(fā)器的輸入。如果任何一個(gè)BEFORE
或INSTEAD OF
觸發(fā)器返回NULL
,該操作將在該行上被禁用并且對(duì)于該行不會(huì)觸發(fā)后續(xù)的觸發(fā)器。
一個(gè)觸發(fā)器定義也能指定一個(gè)布爾的WHEN
條件,它將被測(cè)試來(lái)看該觸發(fā)器是否應(yīng)該被觸發(fā)。在行級(jí)觸發(fā)器中,WHEN
條件可以檢查該行的舊列值和/或新列值(語(yǔ)句級(jí)觸發(fā)器也能有WHEN
條件,但是該特性對(duì)它們不太有用)。在一個(gè)BEFORE
觸發(fā)器中,WHEN
條件只是在該函數(shù)被或者將被執(zhí)行前計(jì)算,因此使用
WHEN
條件與在該觸發(fā)器函數(shù)的開(kāi)始測(cè)試相同的條件沒(méi)有本質(zhì)區(qū)別。不過(guò),在一個(gè)AFTER
觸發(fā)器中,WHEN
條件只是在行更新發(fā)生之后被計(jì)算,并且它決定在語(yǔ)句的末尾一個(gè)事件是否被排隊(duì)來(lái)觸發(fā)該觸發(fā)器。因此當(dāng)一個(gè)AFTER
觸發(fā)器的WHEN
不返回真時(shí),在語(yǔ)句的末尾沒(méi)有必要將一個(gè)事件進(jìn)行排隊(duì),也沒(méi)有必要重新取出該行。如果觸發(fā)器只對(duì)少數(shù)行觸發(fā),這可以使得修改很多行的語(yǔ)句明顯加快。
INSTEAD OF
觸發(fā)器不支持WHEN
條件。
通常,行級(jí)BEFORE
被用來(lái)檢查或修改即將被插入或更新的數(shù)據(jù)。例如,一個(gè)BEFORE
觸發(fā)器可以被用來(lái)把當(dāng)前時(shí)間插入到一個(gè)timestamp
列中,或者檢查該行的兩個(gè)元素之間是否一致。行級(jí)AFTER
觸發(fā)器大多數(shù)被用來(lái)將更新傳播到其他表,或者針對(duì)其他表進(jìn)行一致性檢查。進(jìn)行這種工作分工的原因是,一個(gè)
AFTER
觸發(fā)器可以肯定它看到的是該行的最終值,而一個(gè)BEFORE
觸發(fā)器則不能,因?yàn)檫€可能有其他BEFORE
觸發(fā)器在它之后觸發(fā)。如果你不知道讓一個(gè)觸發(fā)器是BEFORE
或AFTER
,則BEFORE
形式更加有效,因?yàn)殛P(guān)于該操作的信息直到語(yǔ)句的末尾都不需要被保存。
如果一個(gè)觸發(fā)器函數(shù)執(zhí)行 SQL 命令,則這些命令可能會(huì)再次引發(fā)觸發(fā)器。這就是所謂的級(jí)聯(lián)觸發(fā)器。對(duì)于級(jí)聯(lián)的層數(shù)沒(méi)有直接的限制。級(jí)聯(lián)有可能會(huì)導(dǎo)致對(duì)同一個(gè)觸發(fā)器的遞歸調(diào)用。例如,一個(gè)INSERT
觸發(fā)器可能執(zhí)行一個(gè)向同一個(gè)表插入一個(gè)額外行的命令,這就導(dǎo)致該INSERT
觸發(fā)器被再次引發(fā)。所以在這種情形下,觸發(fā)器程序員應(yīng)該負(fù)責(zé)避免無(wú)限遞歸。
在定義一個(gè)觸發(fā)器時(shí),可以為它指定參數(shù)。在觸發(fā)器定義中包括參數(shù)的目的是允許具有相似需求的不同觸發(fā)器調(diào)用同一個(gè)函數(shù)。例如,可能有一個(gè)一般性的觸發(fā)器函數(shù),它需要兩個(gè)列名作為參數(shù),一個(gè)放當(dāng)前用戶而另一個(gè)放當(dāng)前時(shí)間戳。在正確編寫的情況下,這個(gè)觸發(fā)器函數(shù)應(yīng)該獨(dú)立于它所觸發(fā)的表。因此同一個(gè)函數(shù)可以被用于具有適當(dāng)列的任意表上的INSERT
事件,這樣做的用途之一是可以自動(dòng)追蹤一個(gè)交易表中記錄的創(chuàng)建。如果被定義成一個(gè)UPDATE
觸發(fā)器,它也可以被用來(lái)追蹤最新的更新事件。
每一種支持觸發(fā)器的編程語(yǔ)言都有自己的方法來(lái)讓觸發(fā)器輸入數(shù)據(jù)對(duì)觸發(fā)器函數(shù)可用。這種輸入數(shù)據(jù)包括觸發(fā)器事件的類型(如INSERT
或UPDATE
)以及被列在CREATE TRIGGER
中的任何參數(shù)。對(duì)于一個(gè)行級(jí)觸發(fā)器,輸入數(shù)據(jù)還包括用于INSERT
和UPDATE
觸發(fā)器的
NEW
行,和/或用于UPDATE
和DELETE
觸發(fā)器的OLD
行。語(yǔ)句級(jí)觸發(fā)器當(dāng)前沒(méi)有任何方法檢查被語(yǔ)句修改的單個(gè)行。
默認(rèn)情況下,語(yǔ)句級(jí)觸發(fā)器沒(méi)有辦法檢查該語(yǔ)句修改的行。但是AFTER STATEMENT
觸發(fā)器可以請(qǐng)求創(chuàng)建傳遞表,這樣可以讓受影響的行集合對(duì)該觸發(fā)器可用。AFTER ROW
觸發(fā)器也可以請(qǐng)求傳遞表,這樣它們可以看到表中的整個(gè)變化,同時(shí)也能看到當(dāng)前引發(fā)它們的個(gè)體行中的變化。檢查傳遞表的方法仍是取決于使用的編程語(yǔ)言,但是通常的方法讓傳遞表變得像觸發(fā)器函數(shù)內(nèi)部發(fā)出的SQL命令能夠訪問(wèn)的只讀臨時(shí)表一樣。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: