Como é feito um disparador T-SQL que é acionado apenas em alterações reais?

9

Eu tenho um gatilho de tabela em UPDATE e INSERT que adiciona uma linha a outra tabela. Ele só precisa adicionar uma linha se uma das quatro colunas for alterada. Tentei usar o IF UPDATE (col) para testar alterações, mas ele tem um ponto cego. Ele apenas testa a entrada de algum valor. Preciso ir mais fundo, preciso comparar os valores antigos e os novos para ver se uma mudança verdadeira ocorreu. Ele tem que trabalhar com INSERT e UPDATE.

No caso de um UPDATE, isso é fácil, porque as tabelas inseridas e excluídas têm valores que posso comparar dentro do gatilho. No entanto, para o INSERT, apenas a tabela de inserção possui valores. Como eu preciso disso tudo no mesmo gatilho, como lidar com esse caso INSERT?

Aqui está o script do gatilho que eu quero modificar:

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
fonte
2
Uma palavra rápida sobre o uso de "IF UPDATE (<column>)". Retorna true se o DML especificar um valor para a coluna, independentemente de o valor realmente ter sido alterado ou não.
Jonathan Fite

Respostas:

18

Você pode manipular INSERT e UPDATE com um operador EXCEPT set. O EXISTS avaliará TRUE apenas se for apenas um INSERT ou se for um UPDATE com valores diferentes para qualquer uma dessas colunas.

IF EXISTS (
           SELECT First_Name, Last_Name, JobCoe, Inactive FROM inserted
           EXCEPT
           SELECT First_Name, Last_Name, JobCoe, Inactive FROM deleted
          )
BEGIN...
SQLRaptor
fonte
Isso é muito mais elegante do que observar as várias funções atualizadas pela coluna. Combinamos isso com algum código de front-end para enviar apenas os valores alterados (após muita discussão). Usar um EXCEPT faz muito mais sentido.
Peter Schott
2
Isso não funciona nos casos em que duas linhas são "trocadas" em uma atualização. Se tivermos dois John Smiths que precisam atualizar seus JobCodes (primeiro John de 1 para 2; segundo John de 2 para 1) - isso indica que nenhuma atualização ocorreu.
Steven Hibble 29/07/19
2
@StevenHibble - Embora possível, quão provável é que isso ocorra? Esse caso pode ser facilmente solucionado, incluindo as colunas PK nas instruções Select acima.
Chad Estes
11
Eu diria que a probabilidade depende da fonte de dados e da probabilidade de entrada incorreta de dados. "Opa, John Smith errado ..." não parece que isso nunca iria acontecer. De qualquer forma, isso não aborda a outra metade de uma atualização de várias linhas: como você insere apenas as linhas que mudam? Isso EXISTSverifica se qualquer linha foi alterada. Se você mantiver a inserção da pergunta, registrará todas as linhas atualizadas quando apenas uma for alterada de maneira significativa.
Steven Hibble 29/07/19
2

Caso uma atualização possa afetar várias linhas, você deve se proteger de duas coisas:

  1. Queremos considerar as atualizações que trocam valores entre linhas semelhantes. Se houver dois John Smiths que precisam atualizar seus códigos de trabalho (primeiro John de 1 para 2; segundo John de 2 para 1), precisamos ter cuidado para dizer que ambos foram atualizados.
  2. Queremos apenas registrar as linhas alteradas AT_Person_To_Push. Se 5 linhas são atualizadas, mas apenas 2 são atualizadas da maneira que mais nos interessa, precisamos processar apenas as 2 linhas relevantes.

Aqui está como eu lidaria com isso:

  1. Esquerda ingressar insertedem deleted, porque insertedterá linhas para inserções e atualizações, enquanto deletedsó terá linhas para atualizações.
  2. Use EXISTScom EXCEPTpara encontrar linhas em que os insertedvalores diferem dos deletedvalores. Você não pode usar i.First_Name != d.First_Name OR i.Last_Name != d.Last_Name...porque a tabela excluída estará vazia (e o LEFT JOIN retornará nulos) quando o gatilho estiver manipulando um INSERT.
  3. Inserir apenas as linhas afetadas 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
Steven Hibble
fonte
1

Tente isso,

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
fonte