Eu tenho um gatilho UPDATE em uma tabela que observa uma coluna específica mudando de um valor específico para qualquer outro valor. Quando isso acontece, ele atualiza alguns dados relacionados em outra tabela por meio de uma única instrução UPDATE.
A primeira coisa que o gatilho faz é verificar se alguma linha atualizada teve o valor desta coluna alterado do valor em questão. Ele simplesmente une INSERTED a DELETED e compara o valor nessa coluna. Se nada se qualificar, ele sai antes do tempo para que a instrução UPDATE não seja executada.
IF NOT EXISTS (
SELECT TOP 1 i.CUSTNMBR
FROM INSERTED i
INNER JOIN DELETED d
ON i.CUSTNMBR = d.CUSTNMBR
WHERE d.CUSTCLAS = 'Misc'
AND i.CUSTCLAS != 'Misc'
)
RETURN
Nesse caso, CUSTNMBR é a chave primária da tabela subjacente. Se eu fizer uma grande atualização nesta tabela (digamos, mais de 5.000 linhas), essa instrução levará AGES, mesmo que eu não tenha tocado na coluna CUSTCLAS. Posso observá-lo parar nesta instrução por vários minutos no Profiler.
O plano de execução é bizarro. Ele mostra uma varredura inserida com 3.714 execuções e aproximadamente 18,5 milhões de linhas de saída. Isso passa por um filtro na coluna CUSTCLAS. Ele une isso (via loop aninhado) a uma varredura excluída (também filtrada em CUSTCLAS), que é executada apenas uma vez e possui 5.000 linhas de saída.
Que coisa idiota estou fazendo aqui para causar isso? Observe que o gatilho absolutamente deve lidar adequadamente com atualizações de várias linhas.
EDITAR :
Eu também tentei escrever assim (no caso de EXISTS estar fazendo algo desagradável), mas ainda é terrível.
DECLARE @CUSTNMBR varchar(31)
SELECT TOP 1 @CUSTNMBR = i.CUSTNMBR
FROM INSERTED i
INNER JOIN DELETED d
ON i.CUSTNMBR = d.CUSTNMBR
WHERE d.CUSTCLAS = 'Misc'
AND i.CUSTCLAS != 'Misc'
IF @CUSTNMBR IS NULL
RETURN
Você poderia avaliar usando explícito
INNER MERGE JOIN
ouINNER HASH JOIN
dicas, mas dado que você provavelmente está usando essas tabelas novamente mais tarde no gatilho, provavelmente é melhor apenas inserir o conteúdo deinserted
edeleted
tabelas em tabelas indexadas#temp
e terminar com isso.Eles não obtêm índices úteis criados para eles automaticamente.
Sei que isso foi respondido, mas acabou de aparecer como ativo recentemente e também encontrei isso em tabelas com muitos milhões de linhas. Apesar de não descartar a resposta aceita, posso pelo menos acrescentar que minha experiência mostra que um fator chave no desempenho do Trigger ao fazer testes semelhantes (ver se uma ou mais colunas realmente tiveram seus valores alterados) é se a(s) coluna(s) sendo testados eram, na verdade, parte da
UPDATE
declaração. Descobri que comparar colunas entre as tabelasinserted
edeleted
que de fato não faziam parte daUPDATE
instrução causava um grande empecilho no desempenho que, de outra forma, não existiria se esses campos fizessem parte doUPDATE
declaração (independentemente de seu valor realmente ser alterado). Por que todo esse trabalho (ou seja, uma consulta para comparar N campos em X linhas) para determinar se algo mudou se você pode descartar logicamente a possibilidade de qualquer uma dessas colunas ser alterada, o que obviamente não é possível se elas não estivessem presentes naSET
cláusula daUPDATE
declaração.A solução que empreguei foi usar a função UPDATE() que só funciona dentro de Triggers. Essa função interna informa se uma coluna foi especificada na
UPDATE
instrução e pode ser usada para sair do Trigger se as colunas com as quais você está preocupado não fizerem parte do arquivoUPDATE
. Isso pode ser usado em conjunto com umSELECT
para determinar se essas colunas, assumindo que estão presentes noUPDATE
, têm alterações reais. Eu tenho um código na parte superior de vários gatilhos de auditoria que se parece com:Essa lógica prosseguirá para o restante do gatilho se:
INSERT
SET
cláusula de umUPDATE
e pelo menos uma dessas colunas em uma linha foi alteradaO
NOT (UPDATE...) OR NOT EXISTS()
pode parecer estranho ou reverso, mas foi projetado para evitar fazer issoSELECT
nas tabelasinserted
edeleted
se nenhuma das colunas relevantes fizer parte doUPDATE
.Dependendo de suas necessidades, a função COLUMNS_UPDATED() é outra opção para determinar quais colunas fazem parte da
UPDATE
instrução.Eu posso tentar reescrever usando se existir
http://dave.brittens.org/blog/writing-well-behaved-triggers.html
De acordo com Dave, você deve usar tabelas temporárias ou variáveis de tabela com índices, porque as tabelas virtuais INSERTED/DELETED não possuem nenhum. Se você tiver a possibilidade de gatilhos recursivos, use variáveis de tabela para evitar colisões de nomes.
Espero que alguém ache isso útil, pois a postagem original foi feita há algum tempo ...
O código a seguir pode aumentar o desempenho desse gatilho. Eu não sabia o tipo de dado correto da coluna [custclass] , então você precisa ajustá-lo.
Observe que você pode incluir colunas adicionais nessas cópias de memória das tabelas inseridas e excluídas se precisar delas em seu código de gatilho. As chaves primárias nessas tabelas aumentarão muito o desempenho da junção ao atualizar mais do que algumas linhas de uma vez. Boa sorte!