J'ai un déclencheur de mise à jour sur une table qui regarde une colonne spécifique passant d'une valeur spécifique à une autre valeur. Lorsque cela se produit, il met à jour certaines données connexes dans une autre table via une seule déclaration de mise à jour.
La première chose que le déclencheur est vérifiant si toutes les lignes mises à jour avaient la valeur de cette colonne passée de la valeur en question. Il se joint simplement inséré à supprimé et compare la valeur dans cette colonne. Si rien ne se qualifie, il bénit tôt, la déclaration de mise à jour ne fonctionne pas.
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
Dans ce cas, CustNMBR est la principale clé de la table sous-jacente. Si je fais une grande mise à jour sur cette table (disons, plus de 5 000 rangées), cette déclaration prend des âges, même si je n'ai pas touché la colonne Custlas. Je peux regarder cela sur cette déclaration pendant plusieurs minutes de profileur.
Le plan d'exécution est bizarre. Il affiche une analyse insérée avec 3 714 exécutions et ~ 18,5 millions de lignes de sortie. Qui traverse un filtre sur la colonne Custlas. Il rejoint ceci (via une boucle imbriquée) à une analyse supprimée (filtrée également sur Captlas), qui n'exerce qu'une seule fois et dispose de 5000 lignes de sortie.
Quelle chose idiote est-ce que je fais ici pour causer cela? Notez que la gâchette doit absolument gérer correctement les mises à jour de plusieurs lignes.
ÉDITER :
J'ai aussi essayé de l'écrire comme ceci (dans le cas où il existe, il faisait quelque chose de désagréable), mais c'est toujours aussi terrible.
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
Vous pouvez évaluer en utilisant explicit INNER MERGE JOIN
ou alors INNER HASH JOIN
Conseils, mais étant donné que vous utilisez à nouveau ces tables plus tard dans la gâchette, vous êtes probablement mieux en train d'insérer le contenu de inserted
et deleted
tables dans indexé #temp
tables et être fait avec elle.
Ils ne reçoivent pas automatiquement les index utiles pour eux.
Je pourrais essayer de réécrire d'utiliser s'il existe
IF EXISTS (SELECT TOP 1 i.CUSTNMBR
FROM INSERTED i
INNER JOIN DELETED d
ON i.CUSTNMBR = d.CUSTNMBR and d.custclass = 'Misc'
WHERE d.CUSTCLAS <>i.CUSTCLAS)
BEGIN
--do your triggerstuff here
END
http://dave.brittens.org/blog/writing-well-behaved-triggers.html
Selon Dave, vous devez utiliser des tables TEMP ou des variables de table avec des index, car les tables virtuelles insérées/supprimées n'ont aucune. Si vous avez la possibilité de déclencheurs récursifs, vous devez utiliser des variables de table pour éviter les collisions de noms.
J'espère que quelqu'un trouve cela utile car le poste d'origine était tout à fait il y a quelque temps ...