首先发帖,请温柔......
当表格更新或插入行时,我需要更新一列,因此我创建了一个触发器(AFTER INSERT, UPDATE
)。问题在于它是递归的,因为插入包含更新语句,从而再次触发触发器。
我还尝试将INSERT
和UPDATE
分成两个不同的触发器,但我遇到sp_settriggerorder()
和trigger_nestlevel()
的问题,因为存在开箱即用的应用程序默认值,因此还有其他触发器。
我的问题是,有没有办法使用IF子句来说明更新是来自应用程序本身还是我的触发器?例如,如果它是我的触发器,那么我很容易就会因为它返回并且它将不再是递归的。
CREATE TRIGGER [dbo].[JobCardMetlInsertUpdateItemDesc]
ON [dbo].[JobCardMetl] AFTER INSERT
AS
BEGIN TRANSACTION [Description]
UPDATE JobCardMetl
SET JobCardMetl.Description = item.Description
FROM JobCardMetl
INNER JOIN item ON JobCardMetl.Item = item.item
WHERE JobCardMetl.RecordDate = (SELECT MAX(JobCardMetl.RecordDate)
FROM JobCardMetl)
COMMIT TRANSACTION [Description]
答案 0 :(得分:3)
您的触发器非常可疑:它没有引用INSERTED
伪注。这意味着你的触发器正在更新不受INSERT影响的记录,总是代码很大。
递归触发器问题的通常解决方案是要小心哪些列正在更新,即。使用UPDATED()
,以及哪些行和自然业务逻辑应该停止递归(即嵌套触发器应该找不到任何更新,因为防护检查不符合条件)。
最终你可以使用逻辑大锤:SET CONTEXT_INFO
和CONTEXT_INFO()
。你检查它,设置它并在触发器中清理它。如果已设置,则表示您已从触发器嵌套。清理部分至关重要。你也祈祷没有其他app / dev做同样的事情,因为每个会话只有一个上下文信息(SQL 2016改进了这一点)。
答案 1 :(得分:0)
您可以检查说明是否仍然与您希望更新的说明不同。如果相同,则不要更新。这样你就可以避免无休止的递归。
此外,对于WHERE
条件,您似乎希望将更新限制为当前插入的记录,但为此您可以使用虚拟INSERTED
表,其中包含已插入的记录
最后,为原子语句启动新事务似乎有些过分。请注意,触发器无论如何都会在触发INSERT
语句执行的事务中执行。
所以把所有这些放在一起,你可以按如下方式设置你的触发器(我假设RecordDate
唯一地标识一条记录 - 将它改为任何主键):
CREATE TRIGGER [dbo].[JobCardMetlInsertUpdateItemDesc]
ON [dbo].[JobCardMetl] AFTER INSERT
AS
UPDATE JobCardMetl
SET j.Description = item.Description
FROM JobCardMetl j
INNER JOIN item ON j.Item = item.item
INNER JOIN INSERTED i ON i.RecordDate = j.RecordDate
WHERE j.Description IS NULL OR j.Description <> item.Description