我尝试优化我的一些处理。我有两个表(作业和存储桶),它们都具有我想要同步的状态。即如果有人更改了作业的状态,那么有时我会更改存储桶的状态,有时当我更改存储桶的状态时,我想更改所连接作业的状态。有时在这方面意味着大多数"取消"但也有从作业到桶的完成/错误。
他们有两种状态的原因是:(a)它们是独立的子系统,例如可以暂停工作,(b)不是每个工作都附加到存储桶;)
我尝试使用2个触发器执行此操作 - 一个在Job上更新,一个在bucket上更新,但似乎只要作业上的触发器更改了存储桶,就会触发存储桶上的触发器。有趣的是,似乎虽然这是一个更新后的触发器,但似乎触发器没有看到另一个表中的更改....这会导致递归。
触发器是:
ALTER TRIGGER [grd].[Job_UpdateBucket]
ON [grd].[Job]
AFTER UPDATE
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
UPDATE
[simstg].[Bucket]
SET
[Status] = i.[Status],
[StatusTimestamp] = GETUTCDATE()
FROM
[simstg].[Bucket] b
JOIN [inserted] i ON (b.[JobRef] = i.[Id])
WHERE
i.[Status] IN ('C', 'F', 'X', 'A')
AND b.[Status] <> i.[Status]
AND b.[Status] NOT IN ('X')
END
和
ALTER TRIGGER [simstg].[Bucket_UpdateJob]
ON [simstg].[Bucket]
AFTER UPDATE
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
UPDATE
[grd].[Job]
SET
[Status] = 'X',
[LastUpdate] = GETUTCDATE()
FROM
[grd].[Job] j
JOIN [inserted] i ON (j.Id = i.JobRef)
WHERE
i.[Status] = 'X'
AND j.[Status] <> i.[Status]
END
这是 - 虽然我做SQL很长一段时间 - 我第一次有这种类型或递归。我认为where条件会避免递归,因为它是一个AFTER触发器,所以如果Job更新桶,则桶触发器应该看到作业中已经改变的状态,所以不应该递归。
这导致两个问题:
1:我错了吗?如果我更新了Job,那么触发器会更改Bucket,那么Bucket上的触发器应该在Job中看到更改的数据,或者?这应该可以避免递归,因为WHERE条件比较了状态与新的不同。
2:还有其他避免递归的方法吗?
答案 0 :(得分:2)
这是记录在案的行为。 The docs state
无论是否有任何表行受到影响,触发任何有效事件时都会触发这些触发器。
因此,即使您确保使用where子句在更新中没有影响任何行,仍会发生触发触发器的更新,依此类推。我建议管理它的最好方法是使用TRIGGER_NESTLEVEL找出你所处的递归级别,例如。
ALTER TRIGGER [simstg].[Bucket_UpdateJob]
ON [simstg].[Bucket]
AFTER UPDATE
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- CHECK TRIGGER NEST LEVEL HERE AND EXIT IF TOO HIGH
IF TRIGGER_NESTLEVEL() > 1
RETURN;
UPDATE
[grd].[Job]
SET
[Status] = 'X',
[LastUpdate] = GETUTCDATE()
FROM
[grd].[Job] j
JOIN [inserted] i ON (j.Id = i.JobRef)
WHERE
i.[Status] = 'X'
AND j.[Status] <> i.[Status]
END
另一个替代方法是使用UPDATE()
函数来查看在运行更新之前是否已更新Status
列,还是更详细,在执行更新之前检查行是否会受到影响:
ALTER TRIGGER [simstg].[Bucket_UpdateJob]
ON [simstg].[Bucket]
AFTER UPDATE
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
IF EXISTS (SELECT 1 FROM inserted)
BEGIN
UPDATE
[grd].[Job]
SET
[Status] = 'X',
[LastUpdate] = GETUTCDATE()
FROM
[grd].[Job] j
JOIN [inserted] i ON (j.Id = i.JobRef)
WHERE
i.[Status] = 'X'
AND j.[Status] <> i.[Status]
END
END