如何控制MSSQL觸發器遞歸
背景
A表UPDATE後,取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 的控制。對於"A表UPDATE 後,取B表某列再次UPDATE A表",如果僅更新A表的某些列才觸發UPDATE B, 並且B 表再次UPDATE A表不會包含A表觸發UPDATE B的那些列,則在A表的觸發器中,使用IF UPDATE(列)來確定是否應該UPDATE B即可。
2. 使用@@NESTLEVEL
該變量值確定嵌套層數。
對於"A表update後,取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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.