Как работает триггер T-SQL, который срабатывает только при реальных изменениях?

9

У меня есть триггер таблицы на UPDATE и INSERT, который добавляет строку в другую таблицу. Нужно только добавить строку, если один из четырех столбцов изменился. Я попытался использовать IF UPDATE (col) для проверки изменений, но у него есть слепое пятно. Это только проверяет, что вошло какое-то значение. Мне нужно пойти глубже, мне нужно сравнить старые и новые значения, чтобы увидеть, произошло ли истинное изменение. Он должен работать как с INSERT, так и с 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
WillG
источник
2
Быстрое слово об использовании «IF UPDATE (<column>)». Он возвращает true, если DML указывает значение для столбца, независимо от того, изменилось ли значение на самом деле или нет.
Джонатан

Ответы:

18

Вы можете обрабатывать как INSERT, так и UPDATE с помощью оператора EXCEPT set. EXISTS будет оценивать как TRUE, только если это просто INSERT, или если это UPDATE с разными значениями для любого из этих столбцов.

IF EXISTS (
           SELECT First_Name, Last_Name, JobCoe, Inactive FROM inserted
           EXCEPT
           SELECT First_Name, Last_Name, JobCoe, Inactive FROM deleted
          )
BEGIN...
SQLRaptor
источник
Это намного элегантнее, чем смотреть на различные функции, обновляемые в колонках. Мы объединили их с некоторым внешним кодом для отправки только измененных значений (после долгих споров). Использование EXCEPT имеет намного больше смысла.
Питер Шотт
2
Это не работает в случаях, когда 2 строки «меняются» в обновлении. Если у нас есть два Джона Смита, которым необходимо обновить свои JobCodes (первый Джон от 1 до 2; второй Джон от 2 до 1) - это означает, что обновление не произошло.
Стивен Хиббл
2
@ StevenHibble - Хотя возможно, насколько вероятно, что это произойдет? Этот случай можно легко исправить, включив столбцы PK в операторы Select выше.
Чад Эстес
1
Я бы сказал, что вероятность зависит от источника данных и вероятности неправильного ввода данных. «Ой, неправильно, Джон Смит ...» не похоже, что это никогда не произойдет. В любом случае, это не относится к другой половине многострочного обновления: как вы убедитесь, что вставляете только те строки, которые меняются? Это EXISTSпроверяет, что любая строка изменилась. Если вы удерживаете вставку в вопросе, вы будете регистрировать все обновленные строки, когда только одна из них будет содержательно изменена.
Стивен Хиббл
2

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

  1. Мы хотим рассмотреть обновления, которые меняют значения между похожими строками. Если есть два Джона Смита, которым необходимо обновить свои JobCodes (первый Джон от 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...потому что удаленная таблица будет пустой (и LEFT JOIN вернет нули), когда триггер обрабатывает INSERT.
  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
KumarHarsh
источник