J'ai la requête ci-dessous:
select databasename
from somedb.dbo.bigtable l where databasename ='someval' and source <>'kt'
and not exists(select 1 from dbo.smalltable c where c.source=l.source)
La requête ci-dessus se termine en trois secondes.
Si la requête ci-dessus renvoie une valeur, nous voulons que la procédure stockée EXIT, donc je l'ai réécrite comme ci-dessous:
If Exists(
select databasename
from somedb.dbo.bigtable l where databasename ='someval' and source <>'kt'
and not exists(select 1 from dbo.smalltable c where c.source=l.source)
)
Begin
Raiserror('Source missing',16,1)
Return
End
Cependant, cela prend 10 minutes.
Je peux réécrire la requête ci-dessus comme ci-dessous, qui se termine également en moins de 3 secondes:
select databasename
from somedb.dbo.bigtable l where databasename ='someval' and source <>'kt'
and not exists(select 1 from dbo.smalltable c where c.source=l.source
if @@rowcount >0
Begin
Raiserror('Source missing',16,1)
Return
End
Le problème avec la réécriture ci-dessus est que la requête ci-dessus fait partie d'une plus grande procédure stockée et renvoie plusieurs jeux de résultats. En C #, nous parcourons chaque jeu de résultats et effectuons un traitement.
Ce qui précède renvoie un jeu de résultats vide, donc si je choisis cette approche, je dois changer mon C # et refaire le déploiement.
Donc ma question est,
pourquoi l'utilisation
IF EXISTS
change- t-elle simplement le plan pour prendre autant de temps?
Voici les détails qui peuvent vous aider et faites-moi savoir si vous avez besoin de détails:
- Créer un tableau et un script de statistiques pour obtenir le même plan que le mien
- Plan d'exécution lente
Plan d'exécution rapide
Plan lent avec Brentozar Coller le plan
Plan rapide avec Brentozar Coller le plan
Remarque: Les deux requêtes sont les mêmes (en utilisant des paramètres), la seule différence est EXISTS
(j'ai peut-être fait quelques erreurs lors de l'anonymat).
Les scripts de création de table sont ci-dessous:
http://pastebin.com/CgSHeqXc - statistiques de petite table
http://pastebin.com/GUu9KfpS - statistiques de grande table
la source
Réponses:
Comme cela a été expliqué par Paul White dans son blog: A l' intérieur de l'Optimiseur: Objectifs de ligne dans la profondeur les
EXISTS
de Introduit un but de la ligne, qui préfèreNESTED LOOPS
ouMERGE JOIN
plusHASH MATCH
Dans votre requête, cela se produit apparemment pour introduire des boucles imbriquées et supprimer le parallélisme, ce qui entraîne un plan plus lent.
Il vous faudra donc probablement trouver un moyen de réécrire votre requête sans utiliser le à
NOT EXISTS
partir de votre requête.Vous pourriez vous en sortir en réécrivant votre requête à l'aide d'un
LEFT OUTER JOIN
et en vérifiant qu'il n'y avait pas de ligne dans la petite table en testantNULL
Vous pouvez également utiliser une
EXCEPT
requête, selon le nombre de champs sur lesquels vous devez comparer, comme ceci:Attention, Aaron Bertrand a un article de blog expliquant pourquoi il préfère PAS EXISTE que vous devriez lire pour voir si d'autres approches fonctionnent mieux et pour être conscient des problèmes potentiels de correction en cas de valeurs NULL.
Questions et réponses connexes: SI EXISTE prend plus de temps que l'instruction select intégrée
la source
Vous devez réécrire votre requête à l'aide de jointures explicites et spécifier l'opération de jointure que vous souhaitez utiliser (boucle, hachage ou fusion) comme celle-ci.
Lorsque vous utilisez EXISTS ou NOT EXISTS, le plan de requête généré par SQL Server avec l'opération NESTED LOOP suppose qu'il doit parcourir une à une toutes les lignes de l'ensemble en recherchant la première ligne pour satisfaire la condition. L'utilisation de HASH JOIN accélérera.
la source
J'ai rencontré le même problème, j'ai réussi à contourner le problème en évitant d'utiliser "EXISTS" et en utilisant la fonction "COUNT ()" et l'instruction "IF ... ELSE".
Pour votre exemple, essayez ce qui suit:
La raison pour laquelle j'ajoute "+ 1" au nombre est pour que je puisse utiliser "> 1" dans la condition IF, l'utilisation de "> 0" ou "<> 0" déclenchera la requête pour utiliser des boucles imbriquées au lieu de HASH Rencontre. Je n'ai pas cherché à savoir pourquoi cela se produit exactement serait intéressant de savoir pourquoi.
J'espère que cela pourra aider!
la source