触发两个表之间的同步状态 - 如何避免递归?

时间:2014-05-28 15:04:43

标签: sql sql-server sql-server-2014

我尝试优化我的一些处理。我有两个表(作业和存储桶),它们都具有我想要同步的状态。即如果有人更改了作业的状态,那么有时我会更改存储桶的状态,有时当我更改存储桶的状态时,我想更改所连接作业的状态。有时在这方面意味着大多数"取消"但也有从作业到桶的完成/错误。

他们有两种状态的原因是:(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:还有其他避免递归的方法吗?

1 个答案:

答案 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