Wyzwalacz aktualizacji SQL tylko wtedy, gdy kolumna jest modyfikowana

101

Patrząc na inne przykłady, wymyśliłem następujące, ale wydaje się, że nie działa tak, jak bym chciał: chcę, aby zaktualizował zmodyfikowane informacje tylko wtedy, gdy QtyToRepairwartość została zaktualizowana ... ale to nie działa że.

Jeśli skomentuję gdzie, to zmodyfikowane informacje są aktualizowane w każdym przypadku. Jak powiedziałem, inne przykłady skłoniły mnie do optymizmu. Wszelkie wskazówki są mile widziane. Dzięki.

Walter

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
Walter de Jong
źródło
8
Ostrzeżenie o update()- sprawdza tylko, czy kolumna pojawia się na liście aktualizacji i zawsze jest prawdziwe dla wstawek. Nie sprawdza, czy wartość kolumny uległa zmianie, ponieważ możesz mieć więcej niż jeden wiersz, w którym niektóre wartości uległy zmianie, a inne nie.
Nikola Markovinović

Odpowiedzi:

128

Masz dwie możliwości odpowiedzi na swoje pytanie:

1- Użyj polecenia aktualizacji w wyzwalaczu.

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- Użyj Połącz między wstawioną tabelą a usuniętą tabelą

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

W przypadku użycia polecenia aktualizacji dla tabeli SCHEDULEi ustawienia QtyToRepairkolumny na nową wartość, jeśli nowa wartość jest równa starej wartości w jednym lub wielu wierszach, rozwiązanie 1 aktualizuje wszystkie zaktualizowane wiersze w tabeli zestawieniowej, ale rozwiązanie 2 aktualizuje tylko wiersze harmonogramu, których stara wartość nie jest równa nowej wartość.

mehdi lotfi
źródło
Przepraszam, że nie wspominam, że to SQLServer. Musiałem użyć usuniętej tabeli. Skończyło się na tym, że napisałem do oddzielnej tabeli (aby zachować historię).
Walter de Jong
9
Podejście nr 2 jest lepsze, jeśli nie chcesz, aby wyzwalacz uruchamiał się, gdy kolumna jest zmieniana na tę samą wartość.
Neolisk
2
Myślę, że może mi czegoś brakuje - ale podejście nr 1 nie zadziała, ponieważ jest to po aktualizacji, więc bieżący wiersz miałby zawsze tę samą wartość co wiersz wstawiania. Państwo musi dołączyć do usunięty jeśli używasz po aktualizacji
Rob
5
@Rob Instrukcja „IF UPDATE” wywołuje procedurę, która ma dostęp do listy kolumn, które zostały zaktualizowane jako część zapytania, a zatem nie musi znać poprzednich wartości. (Właśnie natknęliśmy się na to w naszym środowisku i potwierdziliśmy, że „IF UPDATE” jest honorowane niezależnie od klauzuli „AFTER UPDATE”)
TChadwick,
3
dlaczego dołączasz do insertedstołu w drugim zapytaniu? powinien być taki sam jak sam stół, prawda?
teran
22

fyi Kod, który otrzymałem:

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
Walter de Jong
źródło
1
Uznałem to za pomocne, ponieważ powyższa odpowiedź 2- Użyj łączenia między nigdy nie działała ze względu na to, że kryteria WHERE S.QtyToRepair <> I.QtyToRepair AND D.QtyToRepair <> I.QtyToRepairnigdy nie były uruchamiane / pasujące, ponieważ pierwsze kryteria nigdy nie były prawdziwe - wstawiona tabela zawsze pasowała do rzeczywistej wartości tabeli. Kluczem dla mnie było użycie `WHERE I.QtyToRepair <> D.QtyToRepair`. Również IF UPDATE (field)pomoc z wyzwalania wielu wyzwalaczy.
Firegarden
13

Należy QtyToRepairnajpierw sprawdzić, czy jest aktualizowany.

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

Chcesz wykonać następujące czynności:

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

Pamiętaj, że ten wyzwalacz będzie uruchamiany za każdym razem, gdy zaktualizujesz kolumnę, niezależnie od tego, czy wartość jest taka sama, czy nie.

Carlos Vergara
źródło
2

Za każdym razem, gdy rekord jest aktualizowany, jest on „usuwany”. Oto mój przykład:

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

To działa dobrze

Mohamed Yazeer
źródło