(Question déplacée de SO)
J'ai une table (données fictives) avec un index cluster contenant 2 colonnes:
Maintenant, je lance ces deux requêtes:
declare
@productid int =1 ,
@priceid int = 1
SELECT productid,
t.priceID
FROM Transactions AS t
WHERE (productID = @productid OR @productid IS NULL)
AND (priceid = @priceid OR @priceid IS NULL)
SELECT productid,
t.priceID
FROM Transactions AS t
WHERE (productID = @productid)
AND (priceid = @priceid)
Le plan d'exécution réel pour les deux requêtes est le suivant:
Comme vous pouvez le voir, le premier utilise SCAN tandis que le second utilise SEEK.
Cependant - en ajoutant OPTION (RECOMPILE)
à la première requête, le plan d'exécution a également utilisé SEEK:
Des amis du chat DBA m'ont dit que:
Dans votre requête, @ productid = 1, ce qui signifie que (productID = @ productID OU @productID IS NULL) peut être simplifié en (productID = @ productID). Le premier nécessite une analyse pour fonctionner avec n'importe quelle valeur de @productID, le second pourrait utiliser une recherche. Ainsi, lorsque vous utilisez RECOMPILE, SQL Server examinera la valeur que vous avez réellement dans @productID et fera le meilleur plan pour cela. Avec une valeur non nulle dans @productID, une recherche est la meilleure. Si la valeur de @productID est inconnue, le plan doit correspondre à toute valeur possible dans @productID, ce qui nécessiterait une analyse. Soyez averti: OPTION (RECOMPILE) forcera une recompilation du plan chaque fois que vous l'exécuterez, ce qui ajoutera quelques millisecondes à chaque exécution. Bien que ce ne soit un problème que si la requête s'exécute très fréquemment.
Aussi :
Si @productID est null, à quelle valeur rechercheriez-vous? Réponse: il n'y a rien à chercher. Toutes les valeurs sont éligibles.
Je comprends que OPTION (RECOMPILE)
SQL Server force à voir quelles sont les valeurs réelles des paramètres et à voir s'il peut RECHERCHER avec.
Mais maintenant, je perds l'avantage de la compilation anticipée.
Question
IMHO - SCAN ne se produira que si un paramètre est nul.
C'est bien - laissez SQL SERVER créer un plan d'exécution pour SCAN.
MAIS si SQL Server voit que j'exécute cette requête plusieurs fois avec des valeurs:, 1,1
alors pourquoi ne crée-t-il PAS UN AUTRE plan d'exécution et utilise SEEK pour cela?
AFAIK - SQL crée un plan d'exécution pour les requêtes les plus consultées .
Pourquoi SQL SERVER n'enregistre-t-il pas un plan d'exécution pour:
@productid int =1 , @priceid int = 1
(Je l'exécute plusieurs fois avec ces valeurs)
- Est-il possible de forcer SQL à conserver ce plan d'exécution (qui utilise SEEK) - pour une invocation future?
la source
Réponses:
Résumant certains des principaux points de notre discussion sur le salon de discussion :
De manière générale, SQL Server met en cache un plan unique pour chaque instruction . Ce plan doit être valide pour toutes les futures valeurs de paramètres possibles .
Il n'est pas possible de mettre en cache un plan de recherche pour votre requête, car ce plan ne serait pas valide si, par exemple, @productid est null.
Dans certaines versions futures, SQL Server pourrait prendre en charge un plan unique qui choisit dynamiquement entre une analyse et une recherche, en fonction des valeurs des paramètres d'exécution, mais ce n'est pas quelque chose que nous avons aujourd'hui.
Classe de problème générale
Votre requête est un exemple de modèle appelé différemment "requête globale" ou "recherche dynamique". Il existe différentes solutions, chacune avec ses avantages et ses inconvénients. Dans les versions modernes de SQL Server (2008+), les principales options sont:
IF
blocsOPTION (RECOMPILE)
sp_executesql
Le travail le plus complet sur le sujet est probablement celui d'Erland Sommarskog, qui est inclus dans les références à la fin de cette réponse. Il n'y a pas moyen de s'éloigner des complexités impliquées, il est donc nécessaire d'investir un certain temps dans l'essai de chaque option pour comprendre les compromis dans chaque cas.
IF
blocsPour illustrer une
IF
solution de bloc pour le cas spécifique de la question:Celui-ci contient une instruction distincte pour les quatre cas possibles nul ou non nul pour chacun des deux paramètres (ou variables locales), il y a donc quatre plans.
Il y a un problème potentiel avec le reniflage de paramètres, qui pourrait nécessiter un
OPTIMIZE FOR
indice sur chaque requête. Veuillez consulter la section des références pour explorer ces types de subtilités.Recompiler
Comme indiqué ci-dessus et dans la question, vous pouvez également ajouter un
OPTION (RECOMPILE)
indice pour obtenir un nouveau plan (rechercher ou analyser) à chaque appel. Étant donné la fréquence relativement lente des appels dans votre cas (une fois toutes les dix secondes en moyenne, avec un temps de compilation inférieur à la milliseconde), il semble probable que cette option vous conviendra:Il est également possible de combiner les fonctionnalités des options ci-dessus de manière créative, pour tirer le meilleur parti des avantages de chaque méthode, tout en minimisant les inconvénients. Il n'y a vraiment aucun raccourci pour comprendre ces choses en détail, puis faire un choix éclairé soutenu par des tests réalistes.
Lectures complémentaires
RECOMPILE
optionsla source