SQL update dispara apenas quando a coluna é modificada

101

Observando outros exemplos, eu vim com o seguinte, mas não parece funcionar como eu gostaria: Eu quero que ele atualize apenas as informações modificadas se o QtyToRepairvalor tiver sido atualizado ... mas não funciona aquele.

Se eu comentar o onde, as informações modificadas serão atualizadas em todos os casos. Como disse outros exemplos me levaram a ser otimista. Quaisquer pistas são apreciadas. Obrigado.

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
fonte
8
Um aviso sobre update()- ele apenas testa se a coluna aparece na lista de atualização e é sempre verdadeiro para inserções. Não verifica se o valor da coluna foi alterado, porque você pode ter mais de uma linha, onde alguns valores foram alterados e outros não.
Nikola Markovinović

Respostas:

128

Você tem dois caminhos para sua pergunta:

1- Use o comando Update em seu Trigger.

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- Use a união entre a tabela inserida e a tabela excluída

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

Ao usar o comando de atualização para a tabela SCHEDULEe definir a QtyToRepaircoluna com o novo valor, se o novo valor for igual ao valor antigo em uma ou várias linhas, a solução 1 atualiza todas as linhas atualizadas na tabela de programação, mas a solução 2 atualiza apenas as linhas de programação cujo valor antigo não é igual ao novo valor.

mehdi lotfi
fonte
Desculpe por não mencionar que era SQLServer. Eu precisava usar a tabela excluída. Acabei escrevendo para uma tabela separada (para manter um histórico).
Walter de Jong
9
A abordagem 2 é melhor, se você não quiser que seu gatilho seja disparado se a coluna estiver sendo alterada para o mesmo valor.
Neolisk
2
Eu acho que pode estar faltando alguma coisa - mas a abordagem nº 1 não funcionaria, porque isso é após a atualização, então a linha atual sempre teria o mesmo valor da linha de inserção. Você deve ingressar para excluído se estiver usando após a atualização
Rob
5
@Rob A instrução "IF UPDATE" está chamando um procedimento que tem acesso a uma lista de colunas de colunas que foram atualizadas como parte da consulta e, portanto, não precisa saber os valores anteriores. (Acabamos de encontrar isso em nosso ambiente e confirmamos que "IF UPDATE" é honrado, independentemente da cláusula "AFTER UPDATE")
TChadwick
3
por que você junta a insertedtabela na segunda consulta? deve ser igual à própria mesa, não é?
teran
22

fyi O código que terminei com:

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
fonte
1
Achei isso útil porque a resposta 2 - Usar junção entre acima nunca funcionou devido ao critério where WHERE S.QtyToRepair <> I.QtyToRepair AND D.QtyToRepair <> I.QtyToRepairnunca disparar / corresponder, pois o primeiro critério nunca foi verdadeiro - a tabela inserida sempre correspondeu ao valor real da tabela. Usar `WHERE I.QtyToRepair <> D.QtyToRepair` foi a chave para mim. Também a IF UPDATE (field)ajuda de vários disparos de gatilhos.
Firegarden de
13

Deve-se verificar se QtyToRepairé atualizado em primeiro lugar.

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
fonte
6

Você deseja fazer o seguinte:

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

Observe que esse acionador será disparado sempre que você atualizar a coluna, independentemente de o valor ser o mesmo ou não.

Carlos Vergara
fonte
2

Sempre que um registro é atualizado, um registro é "excluído". Aqui está meu exemplo:

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

Funciona bem

Mohamed Yazeer
fonte