下面是带有注释的代码片段,用于描述问题陈述。我们有一个更新触发器,在内部调用同一个表上的另一个更新触发器,尽管Recursive Trigger Enabled Property Set为false。
想了解其原因,因为这会对我的应用程序造成严重破坏。
/* Drop statements for the table and triggers*/
IF EXISTS (SELECT * FROM sys.triggers WHERE object_id = OBJECT_ID(N'[dbo]. [t_upd_TestTrigger_002]'))
DROP TRIGGER [dbo].[t_upd_TestTrigger_002]
IF EXISTS (SELECT * FROM sys.triggers WHERE object_id = OBJECT_ID(N'[dbo].[t_upd_TestTrigger_002]'))
DROP TRIGGER [dbo].[t_upd_TestTrigger_001]
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[TestTrigger]') AND type in (N'U'))
DROP TABLE [dbo].[TestTrigger]
CREATE TABLE [dbo].[TestTrigger] /*Creating a test table*/
(
[InternalKey] INT NOT NULL,
[UserModified] varchar(50) DEFAULT SUSER_SNAME()
)
/* Please run the snippet below as seperate batch, else you will get
an error that 'CREATE TRIGGER' must be the first statement in a
query batch.
CREATING A UPDATE TRIGGER FOR THE TEST TABLE
*/
CREATE TRIGGER [t_upd_TestTrigger_001] ON [dbo].[TestTrigger]
FOR UPDATE
AS
BEGIN
--This trigger has some business logic which gets executed
print 'In Trigger 001 '
END
/* Please run the snippet below as separate batch, else you will
get an error that 'CREATE TRIGGER' must be the first statement
in a query batch.
CREATING Another UPDATE TRIGGER FOR THE TEST TABLE
This trigger updates the audit fields in the table and it has to be
a separate trigger, We cannot combine this with other update triggers -
So table TestTrigger will have two FOR UPDATE triggers
*/
CREATE TRIGGER [t_upd_TestTrigger_002] ON [dbo].[TestTrigger]
FOR UPDATE
AS
print 'bad guy starts'
UPDATE SRC
SET UserModified = SUSER_SNAME()
FROM inserted AS INS
INNER JOIN dbo.[TestTrigger] AS SRC
ON INS.InternalKey = SRC.InternalKey
print 'bad guy ends'
/* INSERTING TEST VALUE IN THE TEST TRIGGER TABLE*/
INSERT INTO dbo.[TestTrigger](InternalKey,UserModified)
SELECT 1 ,'Tester1' UNION ALL
SELECT 2,'Tester2' UNION ALL
SELECT 3 ,'Tester3'
/* TestTrigger table has 3 records, we will update the InternalKey
of first record from 1 to 4. We would expect following actions
1) [t_upd_TestTrigger_001] to be executed once
2) [t_upd_TestTrigger_002] to be executed once
3) A message that (1 row(s) affected) only once.
On Execution, i find that [t_upd_TestTrigger_002] internally triggers
[t_upd_TestTrigger_001].
Please note Database level property Recursive Triggers enabled is
set to false.
*/
/*UPDATE THE TABLE SEE THE MESSAGE IN RESULT WINDOW*/
UPDATE dbo.[TestTrigger]
SET InternalKey = 4
WHERE InternalKey = 1
答案 0 :(得分:6)
“启用递归触发器”不会影响传递触发器。
这意味着如果触发器A以激活触发器B的方式更新表,并且触发器B更新同一个表,以便再次运行触发器A,则SQL Server无法检测和禁止此无限循环。特别是因为触发器B可以更新其他表,并且它们上的触发器可以再次更新原始表 - 这可能会变得像你想的那么复杂。
最终,将达到触发器嵌套级别限制,并且循环停止。
我怀疑你的两个触发器都以某种方式更新了源表。如果触发器自行激活,SQL Server只能检测递归触发器。我想你不是这样的。重组触发器是唯一干净的出路。
作为(hackery)的想法:你可以向表中附加一个字段(数据类型和值无关),该字段由无操作但通过触发器更新。然后更改二阶触发器以更新该字段。将该字段的IF UPDATE()
检查添加到您的一阶触发器中。如果已设置字段,则防止现在冗余更新。如果这是有道理的。 ; - )
MSDN: Using Nested Triggers,请参阅“直接递归”和“间接递归”部分。
答案 1 :(得分:0)
如Tomalak所述,您可以使用IF UPDATE(),如果正在更新UserModified,则跳过触发逻辑。
另一种可能性是将UserModified列移动到单独的表中以避免递归。
答案 2 :(得分:0)
如果要完全停止数据库中的这种行为,“要禁用间接递归,请使用sp_configure将嵌套触发器服务器选项设置为0.有关详细信息,请参阅Using Nested Triggers。” 当然,总是需要考虑您实际使用嵌套触发器。