Wie wird ein T-SQL-Trigger, der nur bei echten Änderungen ausgelöst wird, vorgenommen?

9

Ich habe einen Tabellenauslöser für UPDATE und INSERT , der einer anderen Tabelle eine Zeile hinzufügt. Es muss nur eine Zeile hinzugefügt werden, wenn eine von vier Spalten geändert wird. Ich habe versucht, IF UPDATE (col) zu verwenden, um auf Änderungen zu testen, aber es hat einen blinden Fleck. Es wird nur getestet, ob ein Wert eingegangen ist. Ich muss tiefer gehen, ich muss die alten und neuen Werte vergleichen, um festzustellen, ob eine echte Änderung eingetreten ist. Es muss sowohl mit INSERT als auch mit UPDATE funktionieren.

Bei einem UPDATE ist das einfach, da sowohl die eingefügten als auch die gelöschten Tabellen Werte haben, die ich innerhalb des Triggers vergleichen kann. Für das INSERT hat jedoch nur die Insert-Tabelle Werte. Wie gehe ich mit diesem INSERT-Fall um, da ich dies alles im selben Trigger benötige?

Hier ist das Skript des Triggers, den ich ändern möchte:

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
WillG
quelle
2
Ein kurzes Wort zur Verwendung von "IF UPDATE (<Spalte>)". Es gibt true zurück, wenn die DML einen Wert für die Spalte angibt, unabhängig davon, ob sich der Wert tatsächlich geändert hat oder nicht.
Jonathan Fite

Antworten:

18

Sie können sowohl INSERT als auch UPDATE mit einem EXCEPT-Set-Operator verarbeiten. Die EXISTS werden nur dann als TRUE ausgewertet, wenn es sich nur um ein INSERT handelt oder wenn es sich um ein UPDATE mit unterschiedlichen Werten für eine dieser Spalten handelt.

IF EXISTS (
           SELECT First_Name, Last_Name, JobCoe, Inactive FROM inserted
           EXCEPT
           SELECT First_Name, Last_Name, JobCoe, Inactive FROM deleted
          )
BEGIN...
SQLRaptor
quelle
Dies ist viel eleganter als die verschiedenen spaltenaktualisierten Funktionen. Wir haben diese mit einem Front-End-Code kombiniert, um nur die geänderten Werte einzusenden (nach langem Streiten). Die Verwendung eines EXCEPT ist viel sinnvoller.
Peter Schott
2
Dies funktioniert nicht in Fällen, in denen 2 Zeilen in einem Update "vertauscht" werden. Wenn wir zwei John Smiths haben, deren JobCodes aktualisiert werden müssen (erster John von 1 bis 2; zweiter John von 2 bis 1), würde dies bedeuten, dass kein Update stattgefunden hat.
Steven Hibble
2
@StevenHibble - Wie wahrscheinlich ist es, dass dies wahrscheinlich ist? Dieser Fall könnte leicht behoben werden, indem die PK-Spalten in die obigen Select-Anweisungen aufgenommen werden.
Chad Estes
1
Ich würde sagen, die Wahrscheinlichkeit hängt von der Datenquelle und der Wahrscheinlichkeit einer schlechten Dateneingabe ab. "Ups, falscher John Smith ..." scheint nicht so, als würde es niemals passieren. In jedem Fall wird die andere Hälfte eines mehrzeiligen Updates nicht behandelt: Wie stellen Sie sicher, dass nur die Zeilen eingefügt werden, die sich ändern? Dadurch wird EXISTSüberprüft, ob sich eine Zeile geändert hat. Wenn Sie die Einfügung von der Frage fernhalten, protokollieren Sie alle aktualisierten Zeilen, wenn sich nur eine auf sinnvolle Weise ändert.
Steven Hibble
2

Wenn ein Update mehrere Zeilen betreffen kann, müssen Sie sich vor zwei Dingen schützen:

  1. Wir möchten Aktualisierungen in Betracht ziehen, bei denen Werte zwischen ähnlichen Zeilen ausgetauscht werden. Wenn es zwei John Smiths gibt, deren JobCodes aktualisiert werden müssen (erster John von 1 bis 2; zweiter John von 2 bis 1), müssen wir vorsichtig sein, um zu sagen, dass beide aktualisiert wurden.
  2. Wir möchten nur die geänderten Zeilen anmelden AT_Person_To_Push. Wenn 5 Zeilen aktualisiert werden, aber nur 2 auf eine Weise aktualisiert werden, die uns wichtig ist, müssen wir nur die 2 relevanten Zeilen verarbeiten.

So würde ich damit umgehen:

  1. Linke Verknüpfung insertedmit deleted, da insertedZeilen für Einfügungen und Aktualisierungen vorhanden sind, während deletednur Zeilen für Aktualisierungen vorhanden sind.
  2. Verwenden Sie EXISTSmit EXCEPTZeilen zu finden , wo die insertedWerte aus den verschieden deletedWerten. Sie können nicht verwenden, i.First_Name != d.First_Name OR i.Last_Name != d.Last_Name...da die gelöschte Tabelle leer ist (und LEFT JOIN Nullen zurückgibt), wenn der Trigger ein INSERT verarbeitet.
  3. Fügen Sie nur die betroffenen Zeilen in ein 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
Steven Hibble
quelle
1

Versuche dies,

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
KumarHarsh
quelle