実際の変更でのみ発生するT-SQLトリガーはどのように作成されますか?


9

別のテーブルに行を追加するUPDATEおよびINSERTのテーブルトリガーがあります。4つの列のいずれかが変更された場合にのみ行を追加する必要があります。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を返します。
Jonathan Fite、

回答:


18

EXCEPTセット演算子を使用すると、INSERTとUPDATEの両方を処理できます。EXISTSは、単なるINSERTの場合、またはこれらの列のいずれかに異なる値を持つUPDATEの場合にのみ、TRUEと評価されます。

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

これは、さまざまな列更新関数を見るよりもはるかにエレガントです。これらをいくつかのフロントエンドコードと組み合わせて、変更された値のみを送信します(かなりのラングリングの後)。EXCEPTを使用する方がはるかに理にかなっています。
Peter Schott

2
これは、更新で2行が「スワップ」される場合には機能しません。JobCodeを更新する必要がある2人のJohn Smithがいる場合(最初のJohnは1から2、2番目のJohnは2から1)、これは更新が発生していないことを示します。
Steven Hibble

2
@StevenHibble-可能ではありますが、それが発生する可能性はどのくらいありますか?このケースは、上記のSelectステートメントにPK列を含めることで簡単に修正できます。
チャドエステス

1
確率はデータソースと不正なデータ入力の可能性に依存すると思います。「おっと、間違ったジョン・スミス...」というのは決して起こらないとは思えない。いずれにせよ、これは複数行の更新の残りの半分には対応していません。変更された行のみを挿入するようにするにはどうすればよいですか?これEXISTSにより行が変更されたことを確認します。質問からの挿入を維持すると、意味のある方法で1つだけ変更されたときに、更新されたすべての行がログに記録されます。
Steven Hibble

2

更新が複数の行に影響する可能性がある場合は、次の2つのことから保護する必要があります。

  1. 同様の行の間で値を交換する更新を検討します。JobCodeを更新する必要があるJohn Smithが2人いる場合(最初のJohnは1から2に、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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.