Eu tenho um gatilho UPDATE em uma tabela que observa uma coluna específica mudar 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 dessa coluna alterado do valor em questão. Simplesmente associa INSERTED a DELETED e compara o valor nessa coluna. Se nada for qualificado, ele será desativado mais cedo, 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 5000 linhas), essa declaração levará AGES, mesmo que eu não tenha tocado na coluna CUSTCLAS. Eu posso vê-lo parar nesta declaração por vários minutos no Profiler.
O plano de execução é bizarro. Ele mostra uma verificação inserida com 3.714 execuções e ~ 18,5 milhões de linhas de saída. Isso é executado através de um filtro na coluna CUSTCLAS. Ele une isso (via loop aninhado) a uma Varredura excluída (também filtrada no CUSTCLAS), que é executada apenas uma vez e possui 5000 linhas de saída.
Que coisa idiota que estou fazendo aqui para causar isso? Observe que o gatilho absolutamente deve lidar adequadamente com atualizações de várias linhas.
EDIT :
Eu também tentei escrever assim (no caso EXISTS estava fazendo algo desagradável), mas ainda assim é 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
Respostas:
Você pode avaliar o uso explícito
INNER MERGE JOIN
ou deINNER HASH JOIN
dicas, mas, considerando que você provavelmente está usando essas tabelas novamente mais tarde, provavelmente é melhor inserir apenas o conteúdo deinserted
edeleted
tabelas em#temp
tabelas indexadas e concluir o processo.Eles não obtêm índices úteis criados para eles automaticamente.
fonte
CUSTNMBR
para criar o índice clusterizado exclusivo) e usar aOPTION (RECOMPILE)
dica para levá-lo em consideração o número de linhas ou talvez apenas usar uma convenção de nomenclatura específica como#i_dbo_YourTable
#trigger_name_i
. Se eu for com variáveis de tabela, terei que desorganizar ainda mais o código com CREATE TABLEs explícitas. Temos gatilhos em cascata, mas não disparadores recursiva, então eu acho que vou ser seguro ...OPTION (RECOMPILE)
para que a cardinalidade seja levada em consideração.Eu sei que isso foi respondido, mas ele apareceu recentemente como ativo e eu também encontrei isso em tabelas com muitos milhões de linhas. Embora não desconsidere a resposta aceita, posso pelo menos acrescentar que minha experiência mostra que um fator-chave no desempenho do gatilho ao realizar testes semelhantes (ver se uma ou mais colunas realmente tiveram seus valores alterados) é se as colunas são ou não sendo testado eram na verdade parte da
UPDATE
declaração. Descobri que a comparação de colunas entre as tabelasinserted
edeleted
que, de fato, não faziam parte daUPDATE
declaração, causou um enorme impacto no desempenho que, caso contrário, não existia se esses campos fizessem parte doUPDATE
(independentemente do valor realmente ser alterado). Por que todo esse trabalho (ou seja, uma consulta para comparar N campos nas linhas X) para determinar se alguma coisa mudou, se você pode logicamente descartar a possibilidade de alguma dessas colunas ser alterada, o que obviamente não é possível se elas não estiverem presentes naSET
cláusula daUPDATE
declaração.A solução que empreguei foi usar a função UPDATE () , que só funciona dentro dos Triggers. Esta função interna informa se uma coluna foi especificada na
UPDATE
instrução e pode ser usada para sair do Acionador se as colunas que lhe interessam não fizerem parte daUPDATE
. Isso pode ser usado em conjunto com aSELECT
para determinar se essas colunas, supondo que elas estejam presentes naUPDATE
, tenham alterações reais. Eu tenho um código no topo de vários gatilhos de auditoria que se parecem com:Essa lógica continuará para o restante do gatilho se:
INSERT
SET
cláusula deUPDATE
e pelo menos uma dessas colunas em uma linha foi alteradaO
NOT (UPDATE...) OR NOT EXISTS()
pode parecer estranho ou para trás, mas ele é projetado para evitar fazer aSELECT
nosinserted
edeleted
mesas se nenhuma das colunas relevantes fazem parte doUPDATE
.Dependendo das suas necessidades, a função COLUMNS_UPDATED () é outra opção para determinar quais colunas fazem parte da
UPDATE
instrução.fonte
UPDATE(CUSTCLAS)
e pular a coisa toda se falso (+1). Eu não acho que você esteja correto, pois as colunas não atualizadas não estão tão prontamente disponíveis nas versões de linha quanto as versões atualizadas.tempdb
comDBCC PAGE
tempdb
tentei esse script , colei a saída no bloco de notas e procurei "EEEEEE". Eu vejo a saída na captura de tela aqui . Observe as versões anteriores e posteriores das duas colunas nas duas linhas. Pode haver maneiras muito mais fáceis, mas suficientes para meus propósitos aqui!tempdb
páginas próximas aBBBBBB
ouDDDDDD
. Pode ter que fazer mais algumas investigações! Embora talvez isso seja devido àREPLICATE
ligação.Eu poderia tentar reescrever usando se existir
fonte
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 nenhuma. Se você tiver a possibilidade de disparadores recursivos, use variáveis de tabela para evitar colisões de nomes.
Espero que alguém ache isso útil, pois o post original foi há algum tempo ...
fonte
O código a seguir pode aumentar o desempenho desse gatilho. Eu não sabia o tipo de dados 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 no seu código de acionamento. As chaves primárias nessas tabelas aumentarão bastante o desempenho da junção ao atualizar mais de algumas linhas de uma vez. Boa sorte!
fonte