如何在仅进行实际更改时触发T-SQL触发器?


9

在UPDATE和INSERT上有一个表触发器,触发器将行添加到另一个表中。如果更改了四列之一,则只需添加一行。我尝试使用IF UPDATE(col)来测试更改,但它有一个盲点。它仅测试是否引入了一些价值。我需要更深入地研究,我需要比较新旧价值,以查看是否发生了真正的变化。它必须同时使用INSERT和UPDATE。

在UPDATE的情况下很容易,因为插入和删除的表都具有我可以在触发器中比较的值。但是,对于INSERT,只有插入表具有值。因为我需要在同一触发器中完成所有操作,所以如何处理INSERT情况?

这是我要修改的触发器的脚本:

ALTER TRIGGER [dbo].[trATPerson_alter] 
   ON  [mydb].[dbo].[AT_Person]
   AFTER INSERT,UPDATE
AS 
BEGIN
    SET NOCOUNT ON;

    -- Not all updates require a push
    IF (UPDATE([First_Name]) OR UPDATE([Last_Name]) OR UPDATE([JobCode]) OR UPDATE([Inactive]))
    BEGIN
        INSERT INTO [mydb].[dbo].[AT_Person_To_Push] (
                [Facility],
                [VendorID],
                [Person_code],
                [First_Name],
                [Last_Name],
                [JobCode],
                [Alink],
                [Inactive]
            )
        SELECT  [Facility],
                [VendorID],
                [Person_code],
                [First_Name],
                [Last_Name],
                [JobCode],
                [Alink],
                [Inactive]
        FROM inserted 
    END
END

2
快速使用“ IF UPDATE(<column>)”一词。如果DML为列指定值,则返回true,而不管该值是否实际更改。
乔纳森·菲特

Answers:


18

您可以使用EXCEPT集合运算符来处理INSERT和UPDATE。如果EXISTS只是一个INSERT,或者它是对于这些列中的任何一个具有不同值的UPDATE,则EXISTS只会求值为TRUE。

IF EXISTS (
           SELECT First_Name, Last_Name, JobCoe, Inactive FROM inserted
           EXCEPT
           SELECT First_Name, Last_Name, JobCoe, Inactive FROM deleted
          )
BEGIN...

这比查看各种列更新的功能要优雅得多。我们将它们与一些前端代码结合在一起,仅发送更改后的值(经过大量争论)。使用EXCEPT更有意义。
彼得·肖特

2
在更新中“交换”两行的情况下,这不起作用。如果我们有两个需要更新其JobCode的John Smiths(第一个John从1到2;第二个John从2到1)-这将表示没有更新。
史蒂文·希布尔,

2
@StevenHibble-虽然有可能发生这种可能性?通过在上面的Select语句中包含PK列,可以轻松地纠正这种情况。
乍得埃斯特斯

1
我会说概率取决于数据源和不良数据输入的可能性。“哎呀,约翰·史密斯错了……”似乎永远不会发生。无论如何,这不能解决多行更新的另一半问题:如何确保仅插入发生变化的行?这将EXISTS检查是否有任何行更改。如果您不插入问题,那么当只有一个有意义的更改时,您将记录所有更新的行。
史蒂文·希布尔,

2

如果更新可能影响多行,则必须防止两件事:

  1. 我们想要考虑在相似的行之间交换值的更新。如果有两个需要更新其JobCode的John Smiths(第一个John从1到2;第二个John从2到1),我们需要小心地说它们都已更新。
  2. 我们只想将已更改的行记录在中AT_Person_To_Push。如果更新了5行,但仅以我们关心的方式更新了2行,那么我们只需要处理2条相关的行。

这是我的处理方式:

  1. 左联接inserteddeleted,因为inserted将有行用于插入和更新,而deleted仅有行用于更新。
  2. 使用EXISTSwith EXCEPT查找inserted值与值不同的行deleted。您无法使用,i.First_Name != d.First_Name OR i.Last_Name != d.Last_Name...因为当触发器正在处理INSERT时,已删除的表将为空(并且LEFT JOIN将返回null)。
  3. 仅将受影响的行插入AT_Person_To_Push
ALTER TRIGGER [dbo].[trATPerson_alter] 
   ON  [mydb].[dbo].[AT_Person]
   AFTER INSERT,UPDATE
AS 
BEGIN
    SET NOCOUNT ON;

    INSERT INTO [mydb].[dbo].[AT_Person_To_Push] (
            [Facility],
            [VendorID],
            [Person_code],
            [First_Name],
            [Last_Name],
            [JobCode],
            [Alink],
            [Inactive]
        )
    SELECT  i.[Facility],
            i.[VendorID],
            i.[Person_code],
            i.[First_Name],
            i.[Last_Name],
            i.[JobCode],
            i.[Alink],
            i.[Inactive]
    FROM inserted i
         LEFT JOIN deleted d
           ON i.Person_code = d.Person_code
    -- Check for changes that require a push
    WHERE EXISTS (SELECT i.[First_Name], i.[Last_Name], i.[JobCode], i.[Inactive]
                  EXCEPT
                  SELECT d.[First_Name], d.[Last_Name], d.[JobCode], d.[Inactive]);
END

1

尝试这个,

Declare @Acton int=0

If exists (Select 1 from inserted)
set @Acton=1

If exists (Select 1 from deleted)
set @Acton=@Acton+2

if(@Action=1) -- Only insert

if(@Action=3) -- Only Update
begin
IF (UPDATE([First_Name]) OR UPDATE([Last_Name]) OR UPDATE([JobCode]) OR UPDATE([Inactive]))
Begin

End
end

if(@Action=2) -- Only Delete
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.