Chapter 32. 觸發器

Table of Contents
32.1. 觸發器行為概述
32.2. 資料改變的可讀性
32.3. 用 C 寫觸發器
32.4. 一個完整的例子

本章描述如何書寫觸發器函數。 觸發器函數可以用 C 或者任何其它可用的過程語言編寫。 目前不可能用 SQL 語言書寫觸發器。

32.1. 觸發器行為概述

一個觸發器函數可以再一個INSERTUPDATE, 或者 DELETE 命令之前或者之後執行,要麼是對每個被修改的行一次, 要麼是每條 SQL 一次。 如果發生觸發器事件,那麼將在合適的時刻調用觸發器的函數以處理該事件。

觸發器函數必須在建立觸發器之前,作為一個沒有參數並且返回trigger類型的函數定義。 (觸發器函數透過特殊的 TriggerData 結構接收其輸入,而不是用普通函數參數那種形式。)

一旦建立了一個合適的觸發器函數,觸發器就用 CREATE TRIGGER 建立。同一個觸發器函數可以用於多個觸發器。

有兩種類型的觸發器:按行觸發的觸發器和按語句觸發的觸發器。在按行觸發的觸發器裡, 觸發器函數是為觸發觸發器的語句影響的每一行執行一次。相比之下,一個按語句觸發的觸發器是在每執行一次合適的語句執行一次的, 而不管影響的行數。特別是,一個影響零行的語句將仍然導致任何適用的按語句觸發的觸發器的執行。 這兩種類型的觸發器有時候分別叫做"行級別的觸發器""語句級別的觸發器"

語句級別的 "before" 觸發器通常在語句開始做任何事情之前觸發, 而語句級別的 "after" 觸發器在語句的最後觸發。 行級別的 "before" 觸發器在對特定行進行操作的時候馬上觸發, 而行級別的 "after" 觸發器在語句結束的時候觸發(但是在任何語句級別的 "after" 觸發器之前)。

按語句觸發的觸發器應該總是返回 NULL。 如果必要,按行觸發的觸發器函數可以給調用它的執行者返回一資料表資料行(一個類型為 HeapTuple 的數值), 那些在操作之前觸發的觸發器有以下選擇:

一個無意導致任何這類行為的在操作之前觸發的行級觸發器必須仔細返回那個被當作新行傳進來的同一行 (也就是說,對於 INSERTUPDATE 觸發器而言,是 NEW 行, 對於 DELETE 觸發器而言,是 OLD 行)。

對於在操作之後觸發的行級別的觸發器,其返回值會被忽略,因此他們可以返回NULL

如果多於一個觸發器為同樣的事件定義在同樣的關係上, 觸發器將按照由名字的字母順序排序的順序觸發。 如果是事件之前觸發的觸發器,每個觸發器返回的可能已經被修改過的行成為下一個觸發器的輸入。 如果任何事件之前觸發的觸發器返回 NULL 指針, 那麼其操作被丟棄並且隨後的觸發器不會被觸發。

通常,行的 before 觸發器用於檢查或修改將要插入或者更新的資料。 比如,一個 before 觸發器可以用於把目前時間插入一個時間戳字串, 或者跟蹤該行的兩個元素是一致的。行的 after 觸發器多數用於填充或者更新其它資料表, 或者對其它資料表進行一致性檢查。這麼區分工作的原因是, after 觸發器肯定可以看到該行的最後數值, 而 before 觸發器不能;還可能有其它的 before 觸發器在其後觸發。 如果您沒有具體的原因定義觸發器是 before 還是 after,那麼 before 觸發器的效率高些, 因為操作相關的訊息不必保存到語句的結尾。

如果一個觸發器函數執行 SQL 命令,然後這些命令可能再次觸發觸發器。 這就是所謂的級聯觸發器。對級聯觸發器的級聯深度沒有明確的限制。 有可能出現級聯觸發器導致同一個觸發器的遞歸調用的情況; 比如,一個 INSERT 觸發器可能執行一個命令, 把一個額外的行插入同一個資料表中,導致 INSERT 觸發器再次激發。 避免這樣的無窮遞歸的問題是觸發器程序員的責任。

在定義一個觸發器的時候,我們可以聲明一些參數。 在觸發器定義裡面包含參數的目的是允許類似需求的不同觸發器調用同一個函數。 比如,我們可能有一個通用的觸發器函數, 接受兩個字串名字,把目前用戶放在第一個,而目前時間戳在第二個。 只要我們寫得恰當,那麼這個觸發器函數就可以和觸發它的特定資料表無關。 這樣同一個函數就可以用於有著合適字串的任何資料表的 INSERT 事件,實現自動跟蹤交易資料表中的記錄建立之類的問題。如果定義成一個 UPDATE 觸發器,我們還可以用它跟蹤最後更新的事件。

每種支援觸發器的編程語言都有自己的方法讓觸發器函數得到輸入資料。 這些輸入資料包括觸發器事件的類型(比如,INSERT 或者 UPDATE)以及所有在 CREATE TRIGGER 裡面列出的參數。 對於低層次的觸發器,輸入資料也包括 INSERTUPDATE 觸發器的 NEW 行,和/或 UPDATEDELETE 觸發器的 OLD 行。 語句級別的觸發器目前沒有任何方法檢查改語句修改的獨立行。