Zap*_*002 6 trigger sql-server deadlock sql-server-2012 row-modification-time
SQL Server 2012。
每当我的表中的记录发生更改时,我都需要使用当前日期和时间更新列 [LastUpdated]。目前我有:
CREATE TRIGGER Trig_LastUpdated ON Contact AFTER UPDATE
AS
SET NOCOUNT ON
UPDATE ct
SET LastUpdated = GETDATE()
FROM Contact ct
INNER JOIN Inserted i
ON ct.IDContact = i.IDContact
Run Code Online (Sandbox Code Playgroud)
但这是递归的,我不希望这样,会导致死锁和其他奇怪的事情。我无法全局关闭递归触发器。我看到 INSTEAD OF 触发器是非递归的,但是如果我这样做,我是否必须检查 Inserted 中的所有其他列以查看它是否已更新,或者 SQL Server 是否会为我处理?做到这一点的最佳方法是什么?
鉴于您无法禁用递归触发器,下一个最佳选项是:
让触发器使用TRIGGER_NESTLEVEL函数检测其深度有多少。如果它不是堆栈中的第一个触发器执行,请在触发器的开头使用它来简单地退出。大致如下:
IF (TRIGGER_NESTLEVEL() > 1)
BEGIN
-- Uncomment the following PRINT line for debugging
-- PRINT 'Exiting from recursive call to: ' + ISNULL(OBJECT_NAME(@@PROCID), '');
RETURN;
END;
Run Code Online (Sandbox Code Playgroud)
这将需要进行一些测试,以了解另一个触发器完成的初始插入对它有何影响(以防万一成为问题,但也可能不会)。如果它在被另一个触发器调用时未按预期工作,请尝试为此函数设置一些参数。有关详细信息,请参阅文档(上面链接)。
使用SET CONTEXT_INFO在基于会话的“上下文信息”中设置标志。上下文信息是VARBINARY(128)
存在于会话级别的值,并保留其值直到被覆盖或会话结束。可以通过使用CONTEXT_INFO函数或context_info
从以下 DMV 中选择列来检索该值:sys.dm_exec_requests和sys.dm_exec_sessions。
您可以将以下内容放在触发器的开头:
IF (CONTEXT_INFO() = 0x01)
BEGIN
-- Uncomment the following PRINT line for debugging
--PRINT 'Exiting from recursive call to: ' + ISNULL(OBJECT_NAME(@@PROCID), '');
RETURN;
END;
ELSE
BEGIN
-- Uncomment the following PRINT line for debugging
--PRINT 'Initial call to: ' + ISNULL(OBJECT_NAME(@@PROCID), '');
SET CONTEXT_INFO 0x01;
END;
Run Code Online (Sandbox Code Playgroud)
如果您已经出于其他原因使用上下文信息,则此选项效果不太好。但是,任何使用 SQL Server 2016 的人都可以使用SESSION_CONTEXT,它是一组新的基于会话的键值对。
这两种方法都比 using 更可靠,IF NOT UPDATE(LastUpdated)
因为该UPDATE(column_name)
函数只能告诉您该列SET
是否在子句中。它无法告诉您该值是否已更改,或者是否更改为GETDATE()
您期望/想要的“当前”值。这意味着,以下所有语句都绕过了触发器的预期效果(即确保列LastUpdated
具有修改的实际日期和时间):
UPDATE ct
SET ct.LastUpdated = ct.LastUpdated
FROM Contact ct
WHERE ...
UPDATE ct
SET ct.LastUpdated = '1900-04-01`
FROM Contact ct
WHERE ...
UPDATE ct
SET ct.LastUpdated = 1
FROM Contact ct
WHERE ...
UPDATE ct
SET ct.LastUpdated = GETDATE() + 90
FROM Contact ct
WHERE ...
Run Code Online (Sandbox Code Playgroud)
最安全的方法可能是使用TRIGGER_NESTLEVEL()
(选项 1)并传入参数来仅检查此特定触发器,这样由于来自INSERT
另一个触发器的调用不会对其产生不利影响:
TRIGGER_NESTLEVEL( @@PROCID , 'AFTER' , 'DML' )
Run Code Online (Sandbox Code Playgroud)
正如 @ypercube\xe1\xb5\x80\xe1\xb4\xb9 在评论中指出的那样,如何在 SQL Server 2008 R2 表中添加 \xe2\x80\x9clast Updated\xe2\x80\x9d 列?有答案了。检查列是否已更新只是跳过逻辑并按照我期望的方式工作。当用户手动更新列时,会进行更新,并且在不更新时不会导致递归。出于某种原因,我认为它会完全停止更新,但显然情况并非如此。
\n