如何控制MSSQL觸發器遞歸

背景
AUPDATE後,取B表某列再次UPDATE A表,這樣又觸發了A表的 UPDATE  觸發器,我的目的是隻觸發一次,是否設置 nested triggers 選項關閉遞歸觸發器即可?
分析
首先,必須清楚觸發器遞歸的定義,觸發器有兩種遞歸方式:
1.     直接遞歸
A表上的觸發器更改(插入/刪除/更新)A表數據,導致A表的觸發器再次觸發,這種狀況稱之爲直接遞歸;
2.     間接遞歸
A表上的觸發器更新B表數據,導致觸發B表觸發器;而B表觸發器又更改A表數據,導致A表觸發器再次觸發,這種狀況稱之爲間接遞歸
解決方法選項配置(影響所有範圍的觸發器)
SQL Server提供了數據庫級和服務器級配置來確定遞歸觸發器是否被允許:
1.       服務器級(使用存儲過程sp_configure 進行配置)
server trigger recursion 選項(SQL Server 2005)決定是否允許服務器級觸發器直接遞歸激發;當此選項設置爲1 (ON,默認值)時,將允許服務器級觸發器遞歸激發;當設置爲0 (OFF) 時,服務器級觸發器不能遞歸激發。
nested triggers選項決定是否允許觸發器間接遞歸激發;當此選項設置爲1 (ON,默認值)時,將允許觸發器遞歸激發;當設置爲0 (OFF) 時,觸發器不能遞歸激發。
2.       數據庫級
RECURSIVE_TRIGGERS數據庫選項設置決定是否允許數據庫中的觸發器直接遞歸觸發;默認值爲OFF,不允許直接遞歸觸發。
該選項可以通過存儲過程sp_dboption設置;對於SQL Server 2005,還可以使用類似下面的T-SQL設置:
ALTER DATABASE [DbName]
    SET RECURSIVE_TRIGGERS ON
使用選項決定遞歸觸發器的行爲時,需要注意的是選項設置的有效範圍:
nested triggers選項決定所有的觸發器是否間接遞歸激發,這意味着這是一個SQL Server實例級的選項,設置將影響所有的觸發器。
server trigger recursion選項是SQL Server 2005中才有的(SQL Server 2005纔有服務器級觸發器)。
RECURSIVE_TRIGGERS選項影響配置它的數據庫中的所有觸發器。
其他解決方法(針對特定的觸發器)
如果只希望特定的觸發器允許或者禁止觸發器,則SQL Server沒有選項可以做到;如果確實需要這樣的功能,可以在觸發器代碼中實現控制:
1.       使用update(列名)函數
此函數適用於對 UPDATE 的控制。對於"AUPDATE 後,取B表某列再次UPDATE A",如果僅更新A表的某些列才觸發UPDATE B, 並且B 表再次UPDATE A表不會包含A表觸發UPDATE B的那些列,則在A表的觸發器中,使用IF UPDATE()來確定是否應該UPDATE B即可。
2.       使用@@NESTLEVEL
該變量值確定嵌套層數。
對於"Aupdate後,取B表某列再次UPDATE A",如果觸發者不是一個存儲過程, UPDATE A A表觸發器@@NESTLEVEL = 1, UPDATE B, B表觸發器 @@NESTLEVEL = 2, B表觸發器再UPDATE A, @@NESTLEVEL = 3
所以如果 @@NESTLEVEL >=3 , 一般表示遞歸了(當然, 前提是UPDATE A的觸發器本身沒有兩層的遞歸, 即不能是存儲過程再調用存儲過程去UPDATE A
3.       使用 @@PROCID
該全局變量返回調用者的object_id如果需要A表觸發B表觸發器,而B表觸發器再觸發A表觸發器時,A表觸發器不響應;則在A表觸發器中使用它來判斷觸發者是誰,如果是B表觸發器,則不處理就行了,類似下面這樣
IF OBJECT_ID(N'B表觸發器名稱') = @@PROCID
BEGIN
    PRINT 'B表觸發器, 不處理'
    RETURN
END
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章