Триггер обновления SQL только при изменении столбца

101

Посмотрев на другие примеры, я пришел к следующему, но, похоже, он не работает так, как хотелось бы: я хочу, чтобы он обновлял измененную информацию только в том случае, если QtyToRepairзначение было обновлено ... но это не так. который.

Если я закомментирую где, то измененная информация будет обновляться в каждом случае. Как я уже сказал, другие примеры внушали мне оптимизм. Любые подсказки приветствуются. Спасибо.

Уолтер

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
Вальтер де Йонг
источник
8
Предупреждение о update()- он только проверяет, появляется ли столбец в списке обновлений, и всегда верно для вставок. Он не проверяет, изменилось ли значение столбца, потому что у вас может быть более одной строки, где некоторые значения изменились, а некоторые нет.
Никола Марковинович

Ответы:

128

У вас есть два способа ответить на ваш вопрос:

1- Используйте команду обновления в триггере.

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- Используйте соединение между вставленной и удаленной таблицей

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

Когда вы используете команду обновления для таблицы SCHEDULEи устанавливаете QtyToRepairстолбец на новое значение, если новое значение равно старому значению в одной или нескольких строках, решение 1 обновляет всю обновленную строку в таблице расписания, но решение 2 обновляет только строки расписания, старое значение не равно новому стоимость.

Mehdi Lotfi
источник
Извините, что не упомянул, что это был SQLServer. Мне нужно было использовать удаленную таблицу. Я закончил писать в отдельную таблицу (для ведения истории).
Вальтер де Йонг
9
Подход № 2 лучше, если вы не хотите, чтобы ваш триггер срабатывал, если столбец изменяется на то же значение.
Neolisk
2
Я думаю, что мне что-то не хватает, но подход №1 не сработает, потому что это происходит после обновления, поэтому текущая строка всегда будет иметь то же значение, что и строка вставки. Вы должны присоединиться к удаленному, если используете после обновления
Роб
5
@Rob Оператор «IF UPDATE» вызывает процедуру, которая имеет доступ к списку столбцов, которые были обновлены как часть запроса, и, следовательно, не нуждаются в знании предыдущих значений. (Мы только что столкнулись с этим в нашей среде и подтвердили, что «IF UPDATE» соблюдается независимо от пункта «AFTER UPDATE»)
TChadwick
3
почему вы присоединяетесь к insertedтаблице во втором запросе? он должен быть таким же, как и сама таблица, не так ли?
Teran
22

fyi Код, который у меня получился:

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
Вальтер де Йонг
источник
1
Я нашел это полезным, потому что приведенный выше ответ 2- Use Join between никогда не работал из-за того, что критерии WHERE S.QtyToRepair <> I.QtyToRepair AND D.QtyToRepair <> I.QtyToRepairникогда не запускались / не соответствовали, поскольку первый критерий никогда не был истинным - вставленная таблица всегда соответствовала фактическому значению таблицы. Использование `WHERE I.QtyToRepair <> D.QtyToRepair` было для меня ключевым моментом. Также IF UPDATE (field)помощь от срабатывания нескольких триггеров.
Firegarden
13

Следует QtyToRepairсначала проверить, обновляется ли.

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
хгулян
источник
6

Вы хотите сделать следующее:

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

Каждый раз, когда запись обновляется, она «удаляется». Вот мой пример:

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

Отлично работает

Мохамед Язир
источник