只有在修改特定列时才触发SQL更新触发器。

114

通过查看其他示例,我得出了以下结论,但它似乎不像我想要的那样工作:如果已更新QtyToRepair 值,我希望它只更新修改的信息……但它没有这样做。

如果我注释掉 where 子句,那么修改的信息将在所有情况下都会更新。正如我所说,其他示例让我感到乐观。任何线索都会很感激。谢谢。

沃尔特

ALTER TRIGGER [dbo].[tr_SCHEDULE_Modified]
   ON [dbo].[SCHEDULE]
   AFTER UPDATE
AS 
BEGIN
    SET NOCOUNT ON;

    UPDATE SCHEDULE SET modified = GETDATE()
        , ModifiedUser = SUSER_NAME()
        , ModifiedHost = HOST_NAME()
    FROM SCHEDULE S
    INNER JOIN Inserted I on S.OrderNo = I.OrderNo and S.PartNumber = I.PartNumber
    WHERE S.QtyToRepair <> I.QtyToRepair
END

12
关于 update() 的警告 - 它只检查列是否出现在更新列表中,并且对于插入操作始终为真。它不会检查列值是否已更改,因为你可能有多行数据,其中一些值已更改而另一些未更改。 - Nikola Markovinović
6个回答

147

您有两种方式可以处理您的问题:

1- 在触发器中使用Update命令。

ALTER TRIGGER [dbo].[tr_SCHEDULE_Modified]
   ON [dbo].[SCHEDULE]
   AFTER UPDATE
AS BEGIN
    SET NOCOUNT ON;
    IF UPDATE (QtyToRepair) 
    BEGIN
        UPDATE SCHEDULE 
        SET modified = GETDATE()
           , ModifiedUser = SUSER_NAME()
           , ModifiedHost = HOST_NAME()
        FROM SCHEDULE S INNER JOIN Inserted I 
        ON S.OrderNo = I.OrderNo and S.PartNumber = I.PartNumber
        WHERE S.QtyToRepair <> I.QtyToRepair
    END 
END

2- 使用插入表和删除表之间的连接

ALTER TRIGGER [dbo].[tr_SCHEDULE_Modified]
   ON [dbo].[SCHEDULE]
   AFTER UPDATE
AS BEGIN
    SET NOCOUNT ON;    

    UPDATE SCHEDULE 
    SET modified = GETDATE()
       , ModifiedUser = SUSER_NAME()
       , ModifiedHost = HOST_NAME()
    FROM SCHEDULE S 
    INNER JOIN Inserted I ON S.OrderNo = I.OrderNo and S.PartNumber = I.PartNumber
    INNER JOIN Deleted D ON S.OrderNo = D.OrderNo and S.PartNumber = D.PartNumber                  
    WHERE S.QtyToRepair <> I.QtyToRepair
    AND D.QtyToRepair <> I.QtyToRepair
END

当你使用更新命令来更新表SCHEDULEQtyToRepair列为新值时,如果新值在一行或多行中与旧值相等,解决方案1会更新所有更新的Schedule表中的行,但是解决方案2只会更新旧值不等于新值的Schedule行。


11
如果你不希望当列被改变为相同的值时触发器触发,那么方法二更好。 - Victor Zakharov
5
我觉得我可能漏掉了什么——但是方法一行不通,因为这是在更新之后,所以当前行的值总是与插入行相同。如果你使用更新后触发器,你必须连接到“已删除”表。 - Rob
5
“IF UPDATE”语句调用了一个过程,该过程可以访问作为查询的一部分更新的列列表,因此不需要知道先前的值。(我们在我们的环境中遇到了这个问题,并确认“IF UPDATE”被尊重,无论“AFTER UPDATE”子句如何。) - TChadwick
7
为什么你在第二个查询中加入了 inserted 表?它应该和表本身一样,是吗? - teran
根据文档,我对于这个操作是否保证只有在字段改变时才会执行感到困惑。一些应用程序(如EntityFramework)在单个字段可能已更改时会不断覆盖记录的每个字段,而我之前是基于这个触发器只在值改变时才会被执行的假设进行工作的。现在我认为这不是一个安全的假设。 - Dinerdo
显示剩余2条评论

26

补充一下,我最终使用的代码:

IF UPDATE (QtyToRepair)
    begin
        INSERT INTO tmpQtyToRepairChanges (OrderNo, PartNumber, ModifiedDate, ModifiedUser, ModifiedHost, QtyToRepairOld, QtyToRepairNew)
        SELECT S.OrderNo, S.PartNumber, GETDATE(), SUSER_NAME(), HOST_NAME(), D.QtyToRepair, I.QtyToRepair FROM SCHEDULE S
        INNER JOIN Inserted I ON S.OrderNo = I.OrderNo and S.PartNumber = I.PartNumber
        INNER JOIN Deleted D ON S.OrderNo = D.OrderNo and S.PartNumber = D.PartNumber 
        WHERE I.QtyToRepair <> D.QtyToRepair
end

1
我发现这篇文章很有帮助,因为上面的 2- Use Join between 答案从来没有起作用,原因是 where 条件 WHERE S.QtyToRepair <> I.QtyToRepair AND D.QtyToRepair <> I.QtyToRepair 从未触发/匹配,因为第一个条件从未成立 - 插入的表总是与实际表值匹配。对我来说,使用 WHERE I.QtyToRepair <> D.QtyToRepair 是关键。此外,IF UPDATE (field) 的帮助可以防止多个触发器同时触发。 - Firegarden

14

首先应检查是否已更新QtyToRepair

ALTER TRIGGER [dbo].[tr_SCHEDULE_Modified]
   ON [dbo].[SCHEDULE]
   AFTER UPDATE
AS 
BEGIN
SET NOCOUNT ON;
    IF UPDATE (QtyToRepair) 
    BEGIN
        UPDATE SCHEDULE 
        SET modified = GETDATE()
           , ModifiedUser = SUSER_NAME()
           , ModifiedHost = HOST_NAME()
        FROM SCHEDULE S INNER JOIN Inserted I 
            ON S.OrderNo = I.OrderNo and S.PartNumber =    I.PartNumber
        WHERE S.QtyToRepair <> I.QtyToRepair
    END
END

6
您想要做以下事情:
ALTER TRIGGER [dbo].[tr_SCHEDULE_Modified]
   ON [dbo].[SCHEDULE]
   AFTER UPDATE
AS 
BEGIN
SET NOCOUNT ON;

    IF (UPDATE(QtyToRepair))
    BEGIN
        UPDATE SCHEDULE SET modified = GETDATE()
            , ModifiedUser = SUSER_NAME()
            , ModifiedHost = HOST_NAME()
        FROM SCHEDULE S
        INNER JOIN Inserted I ON S.OrderNo = I.OrderNo AND S.PartNumber = I.PartNumber
        WHERE S.QtyToRepair <> I.QtyToRepair
    END
END

请注意,每次更新列时,触发器都会被触发,无论值是否相同。

2
每当一条记录被更新,该记录就会被“删除”。以下是我的示例:
ALTER TRIGGER [dbo].[UpdatePhyDate]
   ON  [dbo].[M_ContractDT1]
   AFTER UPDATE
AS 
BEGIN
    -- on ContarctDT1 PhyQty is updated 
    -- I want system date in Phytate automatically saved
    SET NOCOUNT ON;

    declare @dt1ky as int   

    if(update(Phyqty))
    begin
        select @dt1ky = dt1ky from deleted

        update M_ContractDT1 set PhyDate=GETDATE() where Dt1Ky=  @dt1ky     

    end

END

它运行良好


1
这是不正确的,因为触发器认为它处理一个记录,但实际上它处理的是记录集合。 - ZedZip

0
'after update' 意味着当触发器触发时,表中已经插入了数据。
接受答案中的第二个选项是最佳方法,只需在 where 子句中进行一个修正 - 不等于之前(删除的)值。
WHERE S.QtyToRepair <> D.QtyToRepair

接受答案的选项2,有所更正:
ALTER TRIGGER [dbo].[tr_SCHEDULE_Modified]
   ON [dbo].[SCHEDULE]
   AFTER UPDATE
AS BEGIN
    SET NOCOUNT ON;    

    UPDATE SCHEDULE 
    SET modified = GETDATE()
       , ModifiedUser = SUSER_NAME()
       , ModifiedHost = HOST_NAME()
    FROM SCHEDULE S 
    INNER JOIN Inserted I ON S.OrderNo = I.OrderNo and S.PartNumber = I.PartNumber
    INNER JOIN Deleted D ON S.OrderNo = D.OrderNo and S.PartNumber = D.PartNumber                  
    WHERE S.QtyToRepair <> D.QtyToRepair
    AND D.QtyToRepair <> I.QtyToRepair
END

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接