Renifleur de paramètres
Le paramétrage des requêtes favorise la réutilisation des plans d'exécution mis en cache, évitant ainsi les compilations inutiles et réduisant le nombre de requêtes ad hoc dans le cache du plan.
Ce sont toutes de bonnes choses, à condition la requête paramétrée doit vraiment utiliser le même plan d'exécution mis en cache pour différentes valeurs de paramètres. Un plan d'exécution qui est efficace pour une valeur de paramètre peut ne pas être un bon choix pour d'autres valeurs de paramètres possibles.
Lorsque le reniflage des paramètres est activé (valeur par défaut), SQL Server choisit un plan d'exécution basé sur les valeurs de paramètre particulières qui existent au moment de la compilation. L'hypothèse implicite est que les instructions paramétrées sont le plus souvent exécutées avec les valeurs de paramètre les plus courantes. Cela semble assez raisonnable (voire évident) et en effet cela fonctionne souvent bien.
Un problème peut survenir lors d'une recompilation automatique du plan mis en cache. Une recompilation peut être déclenchée pour toutes sortes de raisons, par exemple parce qu'un index utilisé par le plan mis en cache a été supprimé (une exactitude recompilation) ou parce que les informations statistiques ont changé (une optimalité recompiler).
Quelle que soit la cause exacte de la recompilation du plan, il est possible qu'un élément atypique La valeur est transmise en tant que paramètre au moment où le nouveau plan est généré. Cela peut entraîner un nouveau plan mis en cache (basé sur la valeur de paramètre atypique sniffée) qui n'est pas bon pour la majorité des exécutions pour lesquelles il sera réutilisé.
Il n'est pas facile de prédire quand un plan d'exécution particulier sera recompilé (par exemple, parce que les statistiques ont suffisamment changé), ce qui entraîne une situation où un plan réutilisable de bonne qualité peut être soudainement remplacé par un plan assez différent optimisé pour des valeurs de paramètres atypiques.
Un tel scénario se produit lorsque la valeur atypique est hautement sélective, ce qui se traduit par un plan optimisé pour un petit nombre de lignes. Ces plans utilisent souvent une exécution à thread unique, des jointures de boucles imbriquées et des recherches. De graves problèmes de performances peuvent survenir lorsque ce plan est réutilisé pour différentes valeurs de paramètres qui génèrent un nombre beaucoup plus important de lignes.
Désactivation de la détection des paramètres
Le reniflage de paramètres peut être désactivé à l'aide de l'indicateur de trace documenté 4136. L'indicateur de trace est également pris en charge pour par requête utilisation via le QUERYTRACEON
indice de requête. Les deux s'appliquent à partir de SQL Server 2005 Service Pack 4 (et légèrement plus tôt si vous appliquez des mises à jour cumulatives au Service Pack 3).
À partir de SQL Server 2016, le reniflage de paramètres peut également être désactivé au niveau de la base de données , en utilisant le PARAMETER_SNIFFING
argument de ALTER DATABASE SCOPED CONFIGURATION
.
Lorsque le reniflage des paramètres est désactivé, SQL Server utilise la distribution moyenne statistiques pour choisir un plan d'exécution.
Cela semble également être une approche raisonnable (et peut aider à éviter la situation où le plan est optimisé pour une valeur de paramètre inhabituellement sélective), mais ce n'est pas non plus une stratégie parfaite :un plan optimisé pour une valeur « moyenne » pourrait bien finir par être sérieusement sous-optimal pour les valeurs de paramètres couramment observées.
Considérez un plan d'exécution qui contient des opérateurs gourmands en mémoire comme les tris et les hachages. Étant donné que la mémoire est réservée avant le début de l'exécution de la requête, un plan paramétré basé sur des valeurs de distribution moyennes peut déborder sur tempdb pour les valeurs de paramètre communes qui produisent plus de données que prévu par l'optimiseur.
Les réservations de mémoire ne peuvent généralement pas augmenter pendant l'exécution de la requête, quelle que soit la quantité de mémoire disponible dont dispose le serveur. Certaines applications bénéficient de la désactivation du reniflage des paramètres (voir cet article d'archive par l'équipe Dynamics AX Performance pour un exemple).
Pour la plupart des charges de travail, désactiver entièrement le reniflage des paramètres est la mauvaise solution , et peut même être un désastre. Le reniflage de paramètres est une optimisation heuristique :il fonctionne mieux que l'utilisation de valeurs moyennes sur la plupart des systèmes, la plupart du temps.
Indices de requête
SQL Server fournit une gamme d'indicateurs de requête et d'autres options pour ajuster le comportement du reniflage des paramètres :
- Le
OPTIMIZE FOR (@parameter = value)
l'indicateur de requête crée un plan réutilisable basé sur une valeur spécifique. OPTIMIZE FOR (@parameter UNKNOWN)
utilise des statistiques de distribution moyenne pour un paramètre particulier.OPTIMIZE FOR UNKNOWN
utilise la distribution moyenne pour tous les paramètres (même effet que l'indicateur de trace 4136).- Le
WITH RECOMPILE
L'option de procédure stockée compile un nouveau plan de procédure pour chaque exécution. - L'
OPTION (RECOMPILE)
l'indicateur de requête compile un nouveau plan pour une instruction individuelle.
L'ancienne technique de "masquage des paramètres" (attribuer des paramètres de procédure à des variables locales et référencer les variables à la place) a le même effet que de spécifier OPTIMIZE FOR UNKNOWN
. Il peut être utile sur les instances antérieures à SQL Server 2008 (le OPTIMIZE FOR
l'indice était nouveau pour 2008).
On pourrait faire valoir que chaque L'instruction paramétrée doit être vérifiée pour sa sensibilité aux valeurs des paramètres, et laissée seule (si le comportement par défaut fonctionne bien) ou explicitement indiquée à l'aide de l'une des options ci-dessus.
Cela est rarement fait dans la pratique, en partie parce que la réalisation d'une analyse complète de toutes les valeurs de paramètres possibles peut prendre du temps et nécessite des compétences assez avancées.
Le plus souvent, aucune analyse de ce type n'est effectuée et les problèmes de sensibilité aux paramètres sont traités au fur et à mesure. lorsqu'ils surviennent en production.
Ce manque d'analyse préalable est probablement l'une des principales raisons pour lesquelles le reniflage de paramètres a une mauvaise réputation. Il est utile d'être conscient du potentiel de problèmes et d'effectuer au moins une analyse rapide des instructions susceptibles de causer des problèmes de performances lorsqu'elles sont recompilées avec une valeur de paramètre atypique.
Qu'est-ce qu'un paramètre ?
Certains diront qu'un SELECT
l'instruction référençant une variable locale est une "instruction paramétrée" en quelque sorte, mais ce n'est pas la définition qu'utilise SQL Server.
Une indication raisonnable qu'une déclaration utilise des paramètres peut être trouvée en regardant les propriétés du plan (voir les Paramètres onglet dans Sentry One Plan Explorer. Ou cliquez sur le nœud racine du plan de requête dans SSMS, ouvrez les Propriétés fenêtre et développez la Liste des paramètres nœud):
La « valeur compilée » affiche la valeur reniflée du paramètre utilisé pour compiler le plan mis en cache. La « valeur d'exécution » indique la valeur du paramètre sur l'exécution particulière capturée dans le plan.
L'une ou l'autre de ces propriétés peut être vide ou manquante dans différentes circonstances. Si une requête n'est pas paramétrée, les propriétés seront tout simplement toutes manquantes.
Tout simplement parce que rien n'est jamais simple dans SQL Server, il existe des situations où la liste de paramètres peut être remplie, mais l'instruction n'est toujours pas paramétrée. Cela peut se produire lorsque SQL Server tente un paramétrage simple (discuté plus tard) mais décide que la tentative est « non sécurisée ». Dans ce cas, des marqueurs de paramètres seront présents, mais le plan d'exécution n'est en fait pas paramétré.
Sniffing n'est pas seulement pour les procédures stockées
Le reniflage de paramètres se produit également lorsqu'un lot est explicitement paramétré pour être réutilisé à l'aide de sp_executesql
.
Par exemple :
EXECUTE sys.sp_executesql N' SELECT P.ProductID, P.Name, TotalQty = SUM(TH.Quantity) FROM Production.Product AS P JOIN Production.TransactionHistory AS TH ON TH.ProductID = P.ProductID WHERE P.Name LIKE @NameLike GROUP BY P.ProductID, P.Name; ', N'@NameLike nvarchar(50)', @NameLike = N'K%';
L'optimiseur choisit un plan d'exécution basé sur la valeur reniflée du @NameLike
paramètre. On estime que la valeur du paramètre "K %" correspond à très peu de lignes dans le Product
table, afin que l'optimiseur choisisse une jointure de boucle imbriquée et une stratégie de recherche de clé :
Exécuter à nouveau l'instruction avec une valeur de paramètre de "[H-R]%" (qui correspondra à beaucoup plus de lignes) réutilise le plan paramétré mis en cache :
EXECUTE sys.sp_executesql N' SELECT P.ProductID, P.Name, TotalQty = SUM(TH.Quantity) FROM Production.Product AS P JOIN Production.TransactionHistory AS TH ON TH.ProductID = P.ProductID WHERE P.Name LIKE @NameLike GROUP BY P.ProductID, P.Name; ', N'@NameLike nvarchar(50)', @NameLike = N'[H-R]%';
AdventureWorks L'exemple de base de données est trop petit pour en faire un désastre de performances, mais ce plan n'est certainement pas optimal pour la deuxième valeur de paramètre.
Nous pouvons voir le plan que l'optimiseur aurait choisi en vidant le cache du plan et en exécutant à nouveau la deuxième requête :
Avec un plus grand nombre de correspondances attendues, l'optimiseur détermine qu'une jointure par hachage et une agrégation par hachage sont de meilleures stratégies.
Fonctions T-SQL
Le reniflage de paramètres se produit également avec les fonctions T-SQL, bien que la façon dont les plans d'exécution sont générés puisse rendre cela plus difficile à voir.
Il y a de bonnes raisons d'éviter les fonctions scalaires et multi-instructions T-SQL en général, donc à des fins éducatives uniquement, voici une version de fonction table multi-instructions T-SQL de notre requête de test :
CREATE FUNCTION dbo.F (@NameLike nvarchar(50)) RETURNS @Result TABLE ( ProductID integer NOT NULL PRIMARY KEY, Name nvarchar(50) NOT NULL, TotalQty integer NOT NULL ) WITH SCHEMABINDING AS BEGIN INSERT @Result SELECT P.ProductID, P.Name, TotalQty = SUM(TH.Quantity) FROM Production.Product AS P JOIN Production.TransactionHistory AS TH ON TH.ProductID = P.ProductID WHERE P.Name LIKE @NameLike GROUP BY P.ProductID, P.Name; RETURN; END;
La requête suivante utilise la fonction pour afficher des informations sur les noms de produits commençant par "K" :
SELECT Result.ProductID, Result.Name, Result.TotalQty FROM dbo.F(N'K%') AS Result;
Il est plus difficile de voir le reniflage des paramètres avec une fonction intégrée, car SQL Server ne renvoie pas de plan de requête post-exécution (réel) distinct pour chaque appel de fonction. La fonction pourrait être appelée plusieurs fois dans une seule instruction, et les utilisateurs ne seraient pas impressionnés si SSMS essayait d'afficher un million de plans d'appel de fonction pour une seule requête.
À la suite de cette décision de conception, le plan réel renvoyé par SQL Server pour notre requête de test n'est pas très utile :
Néanmoins, il existe façons de voir le reniflage de paramètres en action avec des fonctions intégrées. La méthode que j'ai choisi d'utiliser ici consiste à inspecter le cache du plan :
SELECT DEQS.plan_generation_num, DEQS.execution_count, DEQS.last_logical_reads, DEQS.last_elapsed_time, DEQS.last_rows, DEQP.query_plan FROM sys.dm_exec_query_stats AS DEQS CROSS APPLY sys.dm_exec_sql_text(DEQS.plan_handle) AS DEST CROSS APPLY sys.dm_exec_query_plan(DEQS.plan_handle) AS DEQP WHERE DEST.objectid = OBJECT_ID(N'dbo.F', N'TF');
Ce résultat montre que le plan de fonction a été exécuté une fois, au prix de 201 lectures logiques avec un temps écoulé de 2891 microsecondes, et que l'exécution la plus récente a renvoyé une ligne. La représentation du plan XML renvoyée montre que la valeur du paramètre était reniflé :
Maintenant, exécutez à nouveau l'instruction, avec un paramètre différent :
SELECT Result.ProductID, Result.Name, Result.TotalQty FROM dbo.F(N'[H-R]%') AS Result;
Le plan de post-exécution montre que 306 lignes ont été renvoyées par la fonction :
La requête de cache de plan montre que le plan d'exécution mis en cache pour la fonction a été réutilisé (execution_count
=2):
Il montre également un nombre beaucoup plus élevé de lectures logiques et un temps écoulé plus long par rapport à l'exécution précédente. Cela est cohérent avec la réutilisation de boucles imbriquées et d'un plan de recherche, mais pour être complètement sûr, le plan de fonction post-exécution peut être capturé à l'aide d'Événements étendus ou le SQL Server Profiler outil :
Étant donné que le reniflage de paramètres s'applique aux fonctions, ces modules peuvent souffrir des mêmes changements de performances inattendus généralement associés aux procédures stockées.
Par exemple, la première fois qu'une fonction est référencée, un plan peut être mis en cache qui n'utilise pas le parallélisme. Les exécutions ultérieures avec des valeurs de paramètres qui bénéficieraient du parallélisme (mais réutiliseraient le plan série mis en cache) afficheront des performances étonnamment médiocres.
Ce problème peut être difficile à identifier car SQL Server ne renvoie pas de plans de post-exécution distincts pour les appels de fonction, comme nous l'avons vu. Utilisation des événements étendus ou Profiler capturer régulièrement des plans post-exécution peut être extrêmement gourmand en ressources, il est donc souvent logique d'utiliser cette technique de manière très ciblée. Les difficultés liées au débogage des problèmes de sensibilité des paramètres de fonction signifient qu'il est encore plus intéressant de faire une analyse (et de coder de manière défensive) avant que la fonction n'entre en production.
Le reniflage de paramètres fonctionne exactement de la même manière avec les fonctions scalaires T-SQL définies par l'utilisateur (sauf si elles sont en ligne, sur SQL Server 2019 et versions ultérieures). Les fonctions table en ligne ne génèrent pas de plan d'exécution séparé pour chaque appel, car (comme leur nom l'indique) elles sont intégrées dans la requête appelante avant la compilation.
Méfiez-vous des NULL reniflés
Videz le cache du plan et demandez une estimation plan (de pré-exécution) pour la requête de test :
SELECT Result.ProductID, Result.Name, Result.TotalQty FROM dbo.F(N'K%') AS Result;
Vous verrez deux plans d'exécution, dont le second est pour l'appel de fonction :
Une limitation du reniflage des paramètres avec des fonctions intégrées dans les plans estimés signifie que la valeur du paramètre est reniflée comme NULL
(pas "K %") :
Dans les versions de SQL Server antérieures à 2012, ce plan (optimisé pour un NULL
paramètre) est mis en cache pour être réutilisé . C'est dommage, car NULL
est peu susceptible d'être une valeur de paramètre représentative, et ce n'était certainement pas la valeur spécifiée dans la requête.
SQL Server 2012 (et versions ultérieures) ne met pas en cache les plans résultant d'une demande de "plan estimé", bien qu'il affiche toujours un plan de fonction optimisé pour un NULL
valeur du paramètre au moment de la compilation.
Paramétrage simple et forcé
Une instruction T-SQL ad hoc contenant des valeurs littérales constantes peut être paramétrée par SQL Server, soit parce que la requête se qualifie pour un paramétrage simple, soit parce que l'option de base de données pour le paramétrage forcé est activée (ou qu'un guide de plan est utilisé dans le même effet).
Une instruction paramétrée de cette manière est également sujette au reniflage de paramètres. La requête suivante se qualifie pour un paramétrage simple :
SELECT A.AddressLine1, A.City, A.PostalCode FROM Person.Address AS A WHERE A.AddressLine1 = N'Heidestieg Straße 8664';
Le plan d'exécution estimé affiche une estimation de 2,5 lignes en fonction de la valeur du paramètre reniflé :
En fait, la requête renvoie 7 lignes (l'estimation de la cardinalité n'est pas parfaite, même lorsque les valeurs sont reniflées) :
À ce stade, vous vous demandez peut-être où se trouve la preuve que cette requête a été paramétrée et que la valeur de paramètre résultante a été reniflée. Exécutez la requête une deuxième fois avec une valeur différente :
SELECT A.AddressLine1, A.City, A.PostalCode FROM Person.Address AS A WHERE A.AddressLine1 = N'Winter der Böck 8550';
La requête renvoie une ligne :
Le plan d'exécution montre que la deuxième exécution a réutilisé le plan paramétré qui a été compilé à l'aide d'une valeur sniffée :
Le paramétrage et le sniffing sont des activités distinctes
Une instruction ad-hoc peut être paramétrée par SQL Server sans les valeurs des paramètres sont reniflées.
Pour démontrer, nous pouvons utiliser l'indicateur de trace 4136 pour désactiver le reniflage des paramètres pour un lot qui sera paramétré par le serveur :
DBCC FREEPROCCACHE; DBCC TRACEON (4136); GO SELECT A.AddressLine1, A.City, A.PostalCode FROM Person.Address AS A WHERE A.AddressLine1 = N'Heidestieg Straße 8664'; GO SELECT A.AddressLine1, A.City, A.PostalCode FROM Person.Address AS A WHERE A.AddressLine1 = N'Winter der Böck 8550'; GO DBCC TRACEOFF (4136);
Le script génère des instructions paramétrées, mais la valeur du paramètre n'est pas reniflée à des fins d'estimation de cardinalité. Pour le voir, nous pouvons inspecter le cache du plan :
WITH XMLNAMESPACES (DEFAULT 'http://schemas.microsoft.com/sqlserver/2004/07/showplan') SELECT DECP.cacheobjtype, DECP.objtype, DECP.usecounts, DECP.plan_handle, parameterized_plan_handle = DEQP.query_plan.value ( '(//StmtSimple)[1]/@ParameterizedPlanHandle', 'NVARCHAR(100)' ) FROM sys.dm_exec_cached_plans AS DECP CROSS APPLY sys.dm_exec_sql_text(DECP.plan_handle) AS DEST CROSS APPLY sys.dm_exec_query_plan(DECP.plan_handle) AS DEQP WHERE DEST.[text] LIKE N'%AddressLine1%' AND DEST.[text] NOT LIKE N'%XMLNAMESPACES%';
Les résultats affichent deux entrées de cache pour les requêtes ad hoc, liées au plan de requête paramétré (préparé) par le descripteur de plan paramétré.
Le plan paramétré est utilisé deux fois :
Le plan d'exécution affiche une estimation de cardinalité différente maintenant que la détection des paramètres est désactivée :
Comparez l'estimation de 1,44571 lignes avec l'estimation de 2,5 lignes utilisée lorsque la détection des paramètres était activée.
Lorsque le reniflage est désactivé, l'estimation provient des informations de fréquence moyenne sur AddressLine1
colonne. Un extrait du DBCC SHOW_STATISTICS
la sortie pour l'index en question montre comment ce nombre a été calculé :la multiplication du nombre de lignes dans le tableau (19 614) par la densité (7,370826e-5) donne l'estimation de 1,44571 lignes.
Remarque : Il est communément admis que seules les comparaisons d'entiers utilisant un index unique peuvent se qualifier pour un paramétrage simple. J'ai délibérément choisi cet exemple (une comparaison de chaînes utilisant un index non unique) pour réfuter cela.
AVEC RECOMPILE et OPTION (RECOMPILE)
Lorsqu'un problème de sensibilité aux paramètres est rencontré, un conseil courant sur les forums et les sites de questions-réponses consiste à "utiliser la recompilation" (en supposant que les autres options de réglage présentées précédemment ne conviennent pas). Malheureusement, ce conseil est souvent interprété à tort comme signifiant l'ajout de WITH RECOMPILE
option à la procédure stockée.
Utilisation de WITH RECOMPILE
nous ramène effectivement au comportement de SQL Server 2000, où la procédure stockée entière est recompilé à chaque exécution.
Une meilleure alternative , sur SQL Server 2005 et versions ultérieures, consiste à utiliser l'OPTION (RECOMPILE)
indice de requête sur la seule instruction qui souffre du problème de reniflage de paramètres. Cet indicateur de requête entraîne une recompilation de l'instruction problématique seul. Les plans d'exécution des autres instructions de la procédure stockée sont mis en cache et réutilisés normalement.
Utilisation de WITH RECOMPILE
signifie également que le plan compilé pour la procédure stockée n'est pas mis en cache. Par conséquent, aucune information de performance n'est conservée dans les DMV telles que sys.dm_exec_query_stats
.
L'utilisation de l'indicateur de requête à la place signifie qu'un plan compilé peut être mis en cache et que les informations de performance sont disponibles dans les DMV (bien qu'elles soient limitées à l'exécution la plus récente, pour l'instruction affectée uniquement).
Pour les instances exécutant au moins SQL Server 2008 build 2746 (Service Pack 1 avec mise à jour cumulative 5), en utilisant OPTION (RECOMPILE)
a un autre avantage significatif sur WITH RECOMPILE
:Uniquement OPTION (RECOMPILE)
active l'optimisation de l'intégration des paramètres .
L'optimisation de l'intégration des paramètres
La détection des valeurs de paramètre permet à l'optimiseur d'utiliser la valeur de paramètre pour dériver des estimations de cardinalité. Les deux WITH RECOMPILE
et OPTION (RECOMPILE)
génèrent des plans de requête avec des estimations calculées à partir des valeurs réelles des paramètres à chaque exécution.
L'optimisation de l'intégration des paramètres pousse ce processus un peu plus loin. Les paramètres de requête sont remplacés avec des valeurs constantes littérales lors de l'analyse de la requête.
L'analyseur est capable de simplifications étonnamment complexes, et l'optimisation ultérieure des requêtes peut encore affiner les choses. Considérez la procédure stockée suivante, qui comporte le WITH RECOMPILE
choix :
CREATE PROCEDURE dbo.P @NameLike nvarchar(50), @Sort tinyint WITH RECOMPILE AS BEGIN SELECT TOP (5) ProductID, Name FROM Production.Product WHERE @NameLike IS NULL OR Name LIKE @NameLike ORDER BY CASE WHEN @Sort = 1 THEN ProductID ELSE NULL END ASC, CASE WHEN @Sort = 2 THEN ProductID ELSE NULL END DESC, CASE WHEN @Sort = 3 THEN Name ELSE NULL END ASC, CASE WHEN @Sort = 4 THEN Name ELSE NULL END DESC; END;
La procédure est exécutée deux fois, avec les valeurs de paramètres suivantes :
EXECUTE dbo.P @NameLike = N'K%', @Sort = 1; GO EXECUTE dbo.P @NameLike = N'[H-R]%', @Sort = 4;
Parce que WITH RECOMPILE
est utilisé, la procédure est entièrement recompilée à chaque exécution. Les valeurs des paramètres sont sniffées à chaque fois, et utilisé par l'optimiseur pour calculer les estimations de cardinalité.
Le plan pour la première exécution de la procédure est tout à fait correct, estimant 1 ligne :
La deuxième exécution estime 360 lignes, très proches des 366 vues à l'exécution :
Les deux plans utilisent la même stratégie d'exécution générale :analysez toutes les lignes d'un index, en appliquant le WHERE
prédicat de clause comme résidu ; calculer le CASE
expression utilisée dans le ORDER BY
clause; et effectuez un Tri Top N sur le résultat du CASE
expression.
OPTION (RECOMPILER)
Recréez maintenant la procédure stockée en utilisant une OPTION (RECOMPILE)
indice de requête au lieu de WITH RECOMPILE
:
CREATE PROCEDURE dbo.P @NameLike nvarchar(50), @Sort tinyint AS BEGIN SELECT TOP (5) ProductID, Name FROM Production.Product WHERE @NameLike IS NULL OR Name LIKE @NameLike ORDER BY CASE WHEN @Sort = 1 THEN ProductID ELSE NULL END ASC, CASE WHEN @Sort = 2 THEN ProductID ELSE NULL END DESC, CASE WHEN @Sort = 3 THEN Name ELSE NULL END ASC, CASE WHEN @Sort = 4 THEN Name ELSE NULL END DESC OPTION (RECOMPILE); END;
L'exécution de la procédure stockée deux fois avec les mêmes valeurs de paramètre qu'auparavant produit des résultats très différents plans d'exécution.
Il s'agit du premier plan d'exécution (avec des paramètres demandant des noms commençant par "K", classés par ProductID
croissant):
L'analyseur intègre les valeurs des paramètres dans le texte de la requête, ce qui donne la forme intermédiaire suivante :
SELECT TOP (5) ProductID, Name FROM Production.Product WHERE 'K%' IS NULL OR Name LIKE 'K%' ORDER BY CASE WHEN 1 = 1 THEN ProductID ELSE NULL END ASC, CASE WHEN 1 = 2 THEN ProductID ELSE NULL END DESC, CASE WHEN 1 = 3 THEN Name ELSE NULL END ASC, CASE WHEN 1 = 4 THEN Name ELSE NULL END DESC;
L'analyseur va ensuite plus loin, supprimant les contradictions et évaluant entièrement le CASE
expressions. Cela se traduit par :
SELECT TOP (5) ProductID, Name FROM Production.Product WHERE Name LIKE 'K%' ORDER BY ProductID ASC, NULL DESC, NULL ASC, NULL DESC;
Vous obtiendrez un message d'erreur si vous essayez de soumettre cette requête directement à SQL Server, car le classement par une valeur constante n'est pas autorisé. Néanmoins, c'est la forme produite par l'analyseur. Il est autorisé en interne car il résulte de l'application de l'optimisation de l'intégration des paramètres . La requête simplifiée facilite grandement la vie de l'optimiseur de requête :
Le balayage d'index groupé applique le LIKE
prédicat comme résidu. Le scalaire de calcul fournit la constante NULL
valeurs. Le Haut renvoie les 5 premières lignes dans l'ordre fourni par l'index clusterisé (en évitant un tri). Dans un monde parfait, l'optimiseur de requête supprimerait également le Compute Scalar qui définit les NULLs
, car ils ne sont pas utilisés lors de l'exécution de la requête.
La deuxième exécution suit exactement le même processus, aboutissant à un plan de requête (pour les noms commençant par les lettres "H" à "R", triés par Name
décroissant) comme ceci :
Ce plan comprend une recherche d'index non clusterisé qui couvre le LIKE
plage, un LIKE
résiduel prédicat, la constante NULLs
comme avant, et un Top (5). L'optimiseur de requête choisit d'effectuer un BACKWARD
balayage de plage dans Index Seek pour encore une fois éviter le tri.
Comparez le plan ci-dessus avec celui produit avec WITH RECOMPILE
, qui ne peut pas utiliser l'optimisation de l'intégration des paramètres :
Cet exemple de démonstration aurait peut-être été mieux implémenté sous la forme d'une série de IF
instructions dans la procédure (une pour chaque combinaison de valeurs de paramètre). Cela pourrait fournir des avantages de plan de requête similaires, sans nécessiter une compilation d'instructions à chaque fois. Dans des scénarios plus complexes, la recompilation au niveau de l'instruction avec l'incorporation de paramètres fournie par OPTION (RECOMPILE)
peut être une technique d'optimisation extrêmement utile.
Une restriction d'intégration
Il existe un scénario où l'utilisation de OPTION (RECOMPILE)
n'entraînera pas l'application de l'optimisation de l'intégration des paramètres. Si l'instruction est affectée à une variable, les valeurs des paramètres ne sont pas intégrées :
CREATE PROCEDURE dbo.P @NameLike nvarchar(50), @Sort tinyint AS BEGIN DECLARE @ProductID integer, @Name nvarchar(50); SELECT TOP (1) @ProductID = ProductID, @Name = Name FROM Production.Product WHERE @NameLike IS NULL OR Name LIKE @NameLike ORDER BY CASE WHEN @Sort = 1 THEN ProductID ELSE NULL END ASC, CASE WHEN @Sort = 2 THEN ProductID ELSE NULL END DESC, CASE WHEN @Sort = 3 THEN Name ELSE NULL END ASC, CASE WHEN @Sort = 4 THEN Name ELSE NULL END DESC OPTION (RECOMPILE); END;
Parce que le SELECT
assigne maintenant à une variable, les plans de requête produits sont les mêmes que lorsque WITH RECOMPILE
a été utilisé. Les valeurs des paramètres sont toujours reniflées et utilisées par l'optimiseur de requête pour l'estimation de la cardinalité, et OPTION (RECOMPILE)
ne compile toujours que la seule instruction, seul l'avantage de l'incorporation de paramètres est perdu.