W jaki sposób wyzwalacz T-SQL uruchamia się tylko przy wprowadzonych rzeczywistych zmianach?

9

Mam wyzwalacz tabeli na UPDATE i INSERT, który dodaje wiersz do innej tabeli. Wystarczy dodać wiersz, jeśli jedna z czterech kolumn zostanie zmieniona. Próbowałem użyć JEŻELI AKTUALIZACJI (kol.) Do testowania zmian, ale ma martwy punkt. Testuje tylko, że pojawiła się pewna wartość. Muszę wejść głębiej, muszę porównać stare i nowe wartości, aby zobaczyć prawdziwą zmianę. Musi współpracować zarówno z INSERT, jak i UPDATE.

W przypadku UPDATE jest to łatwe, ponieważ zarówno wstawione, jak i usunięte tabele mają wartości, które mogę porównać w ramach wyzwalacza. Jednak dla INSERT tylko tabela wstawiania ma wartości. Ponieważ potrzebuję tego wszystkiego w tym samym wyzwalaczu, jak poradzić sobie z tą sprawą INSERT?

Oto skrypt wyzwalacza, który chcę zmodyfikować:

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
źródło
2
Krótkie słowo na temat użycia „IF UPDATE (<kolumna>)”. Zwraca wartość true, jeśli DML określa wartość dla kolumny, niezależnie od tego, czy wartość faktycznie się zmieniła, czy nie.
Jonathan Fite

Odpowiedzi:

18

Możesz obsługiwać zarówno WSTAW, jak i AKTUALIZACJĘ za pomocą operatora zestawu EXCEPT. EXISTS oceni PRAWDA tylko wtedy, gdy jest to WSTAWKA lub AKTUALIZACJA z różnymi wartościami dla dowolnej z tych kolumn.

IF EXISTS (
           SELECT First_Name, Last_Name, JobCoe, Inactive FROM inserted
           EXCEPT
           SELECT First_Name, Last_Name, JobCoe, Inactive FROM deleted
          )
BEGIN...
SQLRaptor
źródło
Jest to o wiele bardziej eleganckie niż patrzenie na różne zaktualizowane kolumny. Połączyliśmy te z jakimś kodem front-end, aby wysyłać tylko zmienione wartości (po wielu sprzeczkach). Korzystanie z WYJĄTKU ma o wiele większy sens.
Peter Schott
2
Nie działa to w przypadkach, w których 2 wiersze są „zamieniane” w aktualizacji. Jeśli mamy dwóch John Smithów, którzy potrzebują zaktualizować swoje JobCodes (pierwszy John od 1 do 2; drugi John od 2 do 1) - to znaczy, że nie nastąpiła żadna aktualizacja.
Steven Hibble
2
@StevenHibble - Choć możliwe, na ile prawdopodobne jest to wystąpienie? Sprawie tej można łatwo zaradzić, włączając kolumny PK w powyższych instrukcjach Select.
Chad Estes
1
Powiedziałbym, że prawdopodobieństwo zależy od źródła danych i prawdopodobieństwa błędnego wprowadzenia danych. „Ups, zły John Smith…” nie wydaje się, żeby to się nigdy nie wydarzyło. W każdym razie nie dotyczy to drugiej połowy aktualizacji z wieloma wierszami: w jaki sposób upewnij się, że wstawiasz tylko te wiersze, które się zmieniają? To EXISTSsprawdza, czy dowolny wiersz się zmienił. Jeśli ukryjesz wstawkę przed pytaniem, będziesz zapisywać wszystkie zaktualizowane wiersze, gdy tylko jedna zmieni się w znaczący sposób.
Steven Hibble
2

Jeśli aktualizacja może wpływać na wiele wierszy, musisz zabezpieczyć się przed dwiema rzeczami:

  1. Chcemy rozważyć aktualizacje, które zamieniają wartości między podobnymi wierszami. Jeśli są dwa John Smiths, którzy potrzebują zaktualizować swoje JobCodes (pierwszy John od 1 do 2; drugi John od 2 do 1), musimy uważać, aby powiedzieć, że oba zostały zaktualizowane.
  2. Chcemy tylko zalogować zmienione wiersze AT_Person_To_Push. Jeśli 5 wierszy zostanie zaktualizowanych, ale tylko 2 zostaną zaktualizowane w sposób, na którym nam zależy, musimy przetworzyć tylko 2 odpowiednie wiersze.

Oto jak bym sobie z tym poradził:

  1. Połącz lewy insertedz deleted, ponieważ insertedbędą miały wiersze dla wstawek i aktualizacji, a deletedbędą miały tylko wiersze dla aktualizacji.
  2. Użyj za EXISTSpomocą, EXCEPTaby znaleźć wiersze, w których insertedwartości różnią się od deletedwartości. Nie możesz użyć, i.First_Name != d.First_Name OR i.Last_Name != d.Last_Name...ponieważ usunięta tabela będzie pusta (a LEFT JOIN zwróci null), gdy wyzwalacz obsługuje INSERT.
  3. Wstaw tylko dotknięte wiersze do 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
źródło
1

Spróbuj tego,

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
źródło