DML 觸發程序
DML 觸發程序經常用來強制執行商務規則和資料完整性。SQL Server 利用 ALTER TABLE 和 CREATE TABLE 陳述式來提供宣告式參考完整性 (DRI)。不過,DRI 並不提供跨資料庫的參考完整性。參考完整性是指資料表主索引鍵和外部索引鍵之間的關聯性規則。若要強制執行參考完整性,請在 ALTER TABLE 和 CREATE TABLE 中,使用 PRIMARY KEY 和 FOREIGN KEY 條件約束。如果觸發程序資料表有條件約束,便會在執行 INSTEAD OF 觸發程序之後,執行 AFTER 觸發程序之前,檢查這些條件約束。如果違反條件約束,便會回復 INSTEAD OF 觸發程序動作,且不會引發 AFTER 觸發程序。
您可以利用 sp_settriggerorder 來指定資料表所要執行的第一個和最後一個 AFTER 觸發程序。一份資料表的每個 INSERT、UPDATE 和 DELETE 作業,都只能指定頭尾各一個 AFTER 觸發程序。如果同一份資料表有其他 AFTER 觸發程序,便會隨機執行它們。
如果 ALTER TRIGGER 陳述式變更第一個或最後一個觸發程序,便會卸除修改的觸發程序所設定的第一個或最後一個屬性,順序值必須利用 sp_settriggerorder 來重設。
只有在觸發的 SQL 陳述式已執行成功之後,才會執行 AFTER 觸發程序。所謂執行成功,包括關聯於已更新或刪除之物件的所有參考重疊顯示動作和條件約束檢查。
如果資料表所定義的 INSTEAD OF 觸發程序是針對通常會再引發 INSTEAD OF 觸發程序的資料表來執行陳述式,這個觸發程序並不稱為遞迴運作。相反地,此時會依照資料表沒有 INSTEAD OF 觸發程序的方式來處理陳述式,且會啟動條件約束作業的鏈結,執行 AFTER 觸發程序。例如,如果觸發程序定義為資料表的 INSTEAD OF INSERT 觸發程序,且這個觸發程序會在相同的資料表上執行 INSERT 陳述式,INSTEAD OF 觸發程序所執行的 INSERT 陳述式便不會重新呼叫這個觸發程序。觸發程序所執行的 INSERT 會啟動執行條件約束動作及引發定義給資料表之任何 AFTER INSERT 觸發程序的程序。
如果檢視所定義的 INSTEAD OF 觸發程序是針對通常會再引發 INSTEAD OF 觸發程序的檢視來執行陳述式,它並不稱為遞迴運作。相反地,陳述式會解析成針對檢視下的基底資料表來進行的修改。在這個情況下,檢視定義必須符合可更新之檢視的所有限制。如需可更新之檢視的定義,請參閱<透過檢視修改資料>。
例如,如果觸發程序定義為檢視的 INSTEAD OF UPDATE 觸發程序,且這個觸發程序會執行參考相同檢視的 UPDATE 陳述式,INSTEAD OF 觸發程序所執行的 UPDATE 陳述式便不會重新呼叫這個觸發程序。觸發程序所執行的 UPDATE 是依照檢視沒有 INSTEAD OF 觸發程序的方式,針對檢視來處理的。UPDATE 所變更的資料行必須解析成單一基底資料表。基礎基底資料表的每項修改都會啟動套用條件約束及引發定義給資料表之 AFTER 觸發程序的鏈結。
測試特定資料行的 UPDATE 或 INSERT 動作
您可以設計一個 Transact-SQL 觸發程序來執行以特定資料行之 UPDATE 或 INSERT 修改為基礎的特定動作。請在觸發程序的主體中,利用 UPDATE() 或 COLUMNS_UPDATED 來完成這個目的。UPDATE() 會測試單一資料行所嘗試的 UPDATE 或 INSERT。COLUMNS_UPDATED 會測試多個資料行所執行的 UPDATE 或 INSERT 動作,且會傳回一個位元模式來指出插入或更新了哪些資料行。
觸發程序限制
CREATE TRIGGER 必須是批次中的第一個陳述式,且只能套用至一份資料表。
觸發程序只會建立在目前資料庫中;不過,觸發程序可以參考在目前資料庫之外的物件。
如果指定觸發程序結構描述名稱來限定觸發程序,請依照相同方式來限定資料表名稱。
您可以在相同 CREATE TRIGGER 陳述式中,定義多個使用者動作 (如 INSERT 和 UPDATE) 的相同觸發程序動作。
您不能在外部索引鍵定義了 DELETE/UPDATE 動作串聯的資料表上,定義 INSTEAD OF DELETE/UPDATE 觸發程序。
您可以在觸發程序內指定任何 SET 陳述式。在觸發程序的執行期間,所選取的 SET 選項會持續有效,之後,會還原為先前的設定。
當引發觸發程序時,如同預存程序一樣,結果會傳回發出呼叫的應用程式。若要防止結果因為引發觸發程序而傳回應用程式,請勿併入會傳回結果的 SELECT 陳述式或是會在觸發程序中執行變數指派的陳述式。包括將結果傳回給使用者的 SELECT 陳述式或執行變數指派的陳述式之觸發程序,需要特殊處理;這些傳回的結果必須寫入允許修改觸發程序資料表的每個應用程式中。如果必須在觸發程序內發生變數指派,請在觸發程序開頭使用 SET NOCOUNT 陳述式,以防止傳回任何結果集。
雖然 TRUNCATE TABLE 陳述式實際上是 DELETE 陳述式,但是它並不會啟動觸發程序,因為此作業不會記錄個別的資料列刪除。但是,只有具有執行 TRUNCATE TABLE 陳述式之權限的使用者才需要擔心這種方式會不小心阻止 DELETE 觸發程序。
WRITETEXT 陳述式,不論有沒有記錄,都不會啟動觸發程序。
在 DML 觸發程序中,不允許使用下列 Transact-SQL 陳述式:
|
ALTER DATABASE
|
CREATE DATABASE
|
DROP DATABASE
|
|
LOAD DATABASE
|
LOAD LOG
|
RECONFIGURE
|
|
RESTORE DATABASE
|
RESTORE LOG
|
|
另外,當 DML 觸發程序所針對的資料表或檢視是觸發動作的目標時,在 DML 觸發程序的主體內,不能有下列 Transact-SQL 陳述式。
|
CREATE INDEX (包括 CREATE SPATIAL INDEX 和 CREATE XML INDEX)
|
ALTER INDEX
|
DROP INDEX
|
|
DBCC DBREINDEX
|
ALTER PARTITION FUNCTION
|
DROP TABLE
|
|
用來執行下列動作的 ALTER TABLE:
-
新增、修改或卸除資料行。
-
切換資料分割。
-
加入或卸除 PRIMARY KEY 或 UNIQUE 條件約束。
|
|
|
附註: |
|---|
|
由於 SQL Server 不支援系統資料表的使用者自訂觸發程序,因此,我們建議您不要在系統資料表上建立使用者自訂觸發程序。
|
DDL 觸發程序
如同標準觸發程序,DDL 觸發程序也會執行預存程序來回應事件。但它們不像標準觸發程序,並不會為了回應資料表或檢視的 UPDATE、INSERT 或 DELETE 陳述式而執行。相反地,執行它們主要是為了回應資料定義語言 (DDL) 陳述式。其中包括 CREATE、ALTER、DROP、GRANT、DENY、REVOKE 和 UPDATE STATISTICS 陳述式。執行類似 DDL 作業的某些系統預存程序也可能引發 DDL 觸發程序。
重要事項: |
|---|
|
請測試 DDL 觸發程序,以判斷它們對執行之系統預存程序的回應。例如,CREATE TYPE 陳述式與 sp_addtype 預存程序,都會引發在 CREATE_TYPE 事件上建立的 DDL 觸發程序。不過,sp_rename 預存程序不會引發任何 DDL 觸發程序。
|
如需有關 DDL 觸發程序的詳細資訊,請參閱<DDL 觸發程序>。
對於影響區域或全域暫存資料表與預存程序的事件,DDL 觸發程序不會為了回應它而引發。
DDL 觸發程序不像 DML 觸發程序,並不以結構描述為範圍。因此,OBJECT_ID、OBJECT_NAME、OBJECTPROPERTY 和 OBJECTPROPERTYEX 無法用來查詢有關 DDL 觸發程序的中繼資料。請改用目錄檢視。如需詳細資訊,請參閱<取得 DDL 觸發程序的相關資訊>。
附註: |
|---|
|
以伺服器為範圍的 DDL 觸發程序出現在 SQL Server Management Studio 物件總管的 [觸發程序] 資料夾中。這個資料夾在 [伺服器物件] 資料夾之下。以資料庫為範圍的 DDL 觸發程序出現在 [資料庫觸發程序] 資料夾中。這個資料夾在對應資料庫的 [可程式性] 資料夾之下。
|
登入觸發程序
登入觸發程序會引發來回應 LOGON 事件。此事件是在使用者工作階段建立時引發。如需詳細資訊,請參閱<登入觸發程序>。
一般觸發程序考量
傳回結果
SQL Server 的未來版本將移除從觸發程序傳回結果的功能。傳回結果集的觸發程序可能會導致非專用的應用程式發生非預期的行為。請避免從新開發工作中的觸發程序傳回結果集,並計畫修改目前採用這種作法的應用程式。若要避免觸發程序傳回結果集,請將 disallow results from triggers 選項設為 1。
登入觸發程序不允許傳回結果集,而且您無法設定這項行為。如果登入觸發程序產生結果集,此觸發程序將無法執行,而引發觸發程序的登入嘗試將被拒絕。
多重觸發程序
SQL Server 允許每個 DML、DDL 或 LOGON 事件各建立多個觸發程序。例如,如果針對已有 UPDATE 觸發程序的資料表來執行 CREATE TRIGGER FOR UPDATE,便會建立其他更新觸發程序。在舊版的 SQL Server 中,每份資料表的每個 INSERT、UPDATE 或 DELETE 資料修改事件都只能有一個觸發程序。
附註: |
|---|
|
當相容性層級是 70 時,CREATE TRIGGER 的預設行為是,如果觸發程序名稱不同,便將其他觸發程序加入現有的觸發程序中。如果觸發程序名稱相同,SQL Server 會傳回錯誤訊息。不過,如果相容性層級等於或小於 65,利用 CREATE TRIGGER 陳述式來建立的任何新觸發程序會取代相同類型的任何現有觸發程序,即使觸發程序名稱不同也是如此。如需詳細資訊,請參閱<sp_dbcmptlevel (Transact-SQL)>。
|
遞迴觸發程序
當利用 ALTER DATABASE 來啟用 RECURSIVE_TRIGGERS 設定時,SQL Server 也允許觸發程序的遞迴叫用。
遞迴觸發程序有可能產生下列遞迴類型:
-
間接遞迴
當進行間接遞迴時,應用程式會更新 T1 資料表。這會引發 TR1 觸發程序,更新 T2 資料表。在這個狀況下,T2 觸發程序後來又會引發和更新 T1 資料表。
-
直接遞迴
當進行直接遞迴時,應用程式會更新 T1 資料表。這會引發 TR1 觸發程序,更新 T1 資料表。由於更新了 T1 資料表,因此,又會引發 TR1 觸發程序,依此類推。
下列範例會使用間接和直接觸發程序遞迴。它假設 T1 資料表定義了 TR1 和 TR2 這兩個更新觸發程序。TR1 觸發程序會遞迴更新 T1 資料表。UPDATE 陳述式會執行每個 TR1 和 TR2 各一次。另外,執行 TR1 會觸發執行 TR1 (遞迴) 和 TR2。特定觸發程序的 inserted 和 deleted 資料表包含只對應於呼叫觸發程序之 UPDATE 陳述式的資料表。
附註: |
|---|
|
只有在利用 ALTER DATABASE 來啟用 RECURSIVE_TRIGGERS 設定時,才會發生上述行為。定義給特定事件的多個觸發程序並沒有任何定義的執行順序。每個觸發程序都應該是獨立自足的。
|
停用 RECURSIVE_TRIGGERS 設定只會防止直接遞迴。如果也要停用間接遞迴,請利用 sp_configure,將 nested triggers 伺服器選項設為 0。
如果有任何觸發程序執行 ROLLBACK TRANSACTION,不論巢狀層級為何,都不會執行任何觸發程序。
巢狀觸發程序
觸發程序的巢狀結構,最多可達 32 層。如果觸發程序變更了一份資料表,且這份資料表有另一個觸發程序,就會啟動第二個觸發程序,之後這個觸發程序可能會呼叫第三個觸發程序,依此類推。如果鏈結中的任何觸發程序造成無限迴圈,就會超出巢狀層級,並取消觸發程序。若要停用巢狀觸發程序,請將 sp_configure 的 nested triggers 選項設成 0 (關閉)。預設組態接受巢狀觸發程序。如果關閉了巢狀觸發程序,遞迴觸發程序也會停用,不論利用 ALTER DATABASE 設定的 RECURSIVE_TRIGGERS 設定為何,都是如此。
附註: |
|---|
|
當 Transact-SQL 觸發程序參考 CLR 常式、類型或彙總來執行 Managed 程式碼時,在 32 層的巢狀限制中,這項參考算是一層。從 Managed 程式碼內叫用的方法,不列入這項限制。
|
延遲名稱解析
SQL Server 允許 Transact-SQL 預存程序、觸發程序和批次參考在編譯階段並不存在的資料表。這項功能稱為延遲名稱解析。不過,如果 Transact-SQL 預存程序、觸發程序或批次參考預存程序或觸發程序所定義的資料表,只有在相容性層級設定是 65 時,才會在建立階段發出警告。如果使用批次的話,便會在編譯階段發出警告。當所參考的資料表不存在時,會在執行階段傳回錯誤訊息。如需詳細資訊,請參閱<延遲名稱解析及編譯>。