web-dev-qa-db-fra.com

Performances atroces Inscrivant des tables insérées et supprimées dans une gâchette

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
12
db2

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.

10
Martin Smith

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
2
HLGEM

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 ...

1
Keith