Як робиться тригер T-SQL, який працює лише на реальні зміни?


9

У мене є тригер таблиці UPDATE та INSERT, який додає рядок до іншої таблиці. Додавати рядок потрібно лише в тому випадку, якщо один з чотирьох стовпців змінено. Я спробував використовувати IF UPDATE (col) для перевірки змін, але у нього є сліпа пляма. Це лише тестує, що якесь значення прийшло. Мені потрібно заглибитись, мені потрібно порівняти старі та нові значення, щоб побачити, чи відбулася справжня зміна. Він повинен працювати як з INSERT, так і з оновленням.

У випадку ОНОВЛЕННЯ це легко, оскільки і вставлені, і видалені таблиці мають значення, які я можу порівняти в триггері. Однак для 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
Швидке слово про використання "АКО ОНОВЛЕНО (<стовпець>)". Він повертає істину, якщо DML вказує значення для стовпця, незалежно від того, змінилося значення чи ні.
Джонатан

Відповіді:


18

Ви можете обробляти як INSERT, так і UPDATE за допомогою оператора набору EXCEPT. ІСНУЮЧІ оцінюватимуть ІСТИНА лише, якщо це лише ВСТАВКА або якщо це ОНОВЛЕННЯ з різними значеннями для будь-якого з цих стовпців.

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

Це набагато елегантніше, ніж дивитися на різні оновлені колонки функції. Ми поєднали їх із деяким передовим кодом, щоб надсилати лише змінені значення (після сильної суперечки). Використання EXCEPT має набагато більше сенсу.
Пітер Шотт

2
Це не спрацьовує у випадках, коли в ряді оновлень "ряд" замінено 2 рядки. Якщо у нас є два Джона Сміта, яким потрібні оновлені коди роботи (перший Джон від 1 до 2; другий Джон з 2 до 1) - це означає, що оновлення не відбулося.
Стівен Хіббл

2
@StevenHibble - Хоча можливо, наскільки ймовірне це відбутися? Цей випадок можна легко виправити, включивши стовпці ПК у викладені оператори Select.
Чад Естес

1
Я б сказав, що ймовірність залежить від джерела даних та ймовірності поганого введення даних. "На жаль, неправильно Джон Сміт ..." не здається, що це ніколи не відбудеться. У будь-якому випадку це не стосується другої половини багаторядкового оновлення: як ви обов'язково вставляєте лише рядки, які змінюються? Це EXISTSперевіряє, чи змінився будь-який рядок. Якщо ви будете зберігати вставку від запитання, ви заносите всі оновлені рядки, коли змістовна зміна лише однієї.
Стівен Хіббл

2

Якщо оновлення може впливати на кілька рядків, вам потрібно захистити від двох речей:

  1. Ми хочемо розглянути оновлення, які змінюють місцями між подібними рядками. Якщо є два Джона Сміта, яким потрібні оновлені коди роботи (перший Джон від 1 до 2; другий Джон з 2 до 1), ми повинні бути обережними, щоб сказати, що вони були оновлені.
  2. Ми хочемо лише увійти в змінені рядки AT_Person_To_Push. Якщо 5 рядків оновлюються, але лише 2 оновлюються таким чином, який нас цікавить, нам потрібно обробити лише 2 відповідних рядки.

Ось як я впорався б із цим:

  1. Залишилося приєднатися insertedдо deleted, тому що insertedбуде мати рядки для вставок та оновлень, тоді як deletedбуде мати рядки лише для оновлень.
  2. Використовуйте EXISTSдля, EXCEPTщоб знайти рядки, де insertedзначення відрізняються від deletedзначень. Ви не можете використовувати, i.First_Name != d.First_Name OR i.Last_Name != d.Last_Name...тому що видалена таблиця буде порожньою (і ВЛІШЕ ПРИЄДНАЙТЕ поверне нулі), коли тригер обробляє ВСТУП.
  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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.