La requête est
SELECT SUM(Amount) AS SummaryTotal
FROM PDetail WITH(NOLOCK)
WHERE ClientID = @merchid
AND PostedDate BETWEEN @datebegin AND @dateend
La table contient 103 129 000 lignes.
Le plan rapide recherche par ClientId un prédicat résiduel sur la date, mais doit effectuer 96 recherches pour extraire le Amount
. La <ParameterList>
section dans le plan est la suivante.
<ParameterList>
<ColumnReference Column="@dateend"
ParameterRuntimeValue="'2013-02-01 23:59:00.000'" />
<ColumnReference Column="@datebegin"
ParameterRuntimeValue="'2013-01-01 00:00:00.000'" />
<ColumnReference Column="@merchid"
ParameterRuntimeValue="(78155)" />
</ParameterList>
Le plan lent recherche par date et comporte des recherches pour évaluer le prédicat résiduel sur ClientId et extraire le montant (1 estimé par rapport à réel 7 388 383). La <ParameterList>
section est
<ParameterList>
<ColumnReference Column="@EndDate"
ParameterCompiledValue="'2013-02-01 23:59:00.000'"
ParameterRuntimeValue="'2013-02-01 23:59:00.000'" />
<ColumnReference Column="@BeginDate"
ParameterCompiledValue="'2013-01-01 00:00:00.000'"
ParameterRuntimeValue="'2013-01-01 00:00:00.000'" />
<ColumnReference Column="@ClientID"
ParameterCompiledValue="(78155)"
ParameterRuntimeValue="(78155)" />
</ParameterList>
Dans ce second cas, le ParameterCompiledValue
n'est pas vide. SQL Server a correctement détecté les valeurs utilisées dans la requête.
Le livre "Dépannage pratique de SQL Server 2005" parle de l'utilisation de variables locales
Utiliser des variables locales pour éliminer les paramètres de détection est une astuce assez courante, mais les astuces OPTION (RECOMPILE)
et OPTION (OPTIMIZE FOR)
... sont généralement des solutions plus élégantes et légèrement moins risquées
Remarque
Dans SQL Server 2005, la compilation au niveau des instructions permet de différer la compilation d'une instruction individuelle dans une procédure stockée jusque juste avant la première exécution de la requête. D'ici là, la valeur de la variable locale sera connue. Théoriquement, SQL Server pourrait en tirer parti pour détecter les valeurs de variables locales de la même manière que les paramètres. Toutefois, comme il était courant d’utiliser des variables locales pour neutraliser le contrôle des paramètres dans SQL Server 7.0 et SQL Server 2000+, le contrôle des variables locales n’était pas activé dans SQL Server 2005. Il pourrait être activé dans une version ultérieure de SQL Server bien raison d'utiliser l'une des autres options décrites dans ce chapitre si vous avez le choix.
À partir d'un test rapide, le comportement décrit ci-dessus est toujours le même en 2008 et 2012 et les variables ne sont pas détectées pour la compilation différée, mais uniquement lorsqu'un OPTION RECOMPILE
indice explicite est utilisé.
DECLARE @N INT = 0
CREATE TABLE #T ( I INT );
/*Reference to #T means this statement is subject to deferred compile*/
SELECT *
FROM master..spt_values
WHERE number = @N
AND EXISTS(SELECT COUNT(*) FROM #T)
SELECT *
FROM master..spt_values
WHERE number = @N
OPTION (RECOMPILE)
DROP TABLE #T
Malgré la compilation différée, la variable n’est pas détectée et le nombre estimé de lignes est inexact.
Je suppose donc que le plan lent concerne une version paramétrée de la requête.
La valeur ParameterCompiledValue
est égale à ParameterRuntimeValue
pour tous les paramètres; il ne s'agit donc pas d'un sniffing de paramètres classique (le plan a été compilé pour un ensemble de valeurs puis exécuté pour un autre ensemble de valeurs).
Le problème est que le plan qui est compilé pour les valeurs de paramètre correctes est inapproprié.
Vous rencontrez probablement des problèmes avec les dates croissantes décrites ici et ici . Pour une table de 100 millions de lignes, vous devez insérer (ou modifier) 20 millions de personnes avant que SQL Server ne mette automatiquement à jour les statistiques. Il semble que la dernière fois qu'ils aient été mis à jour, aucune ligne ne correspond à la plage de dates de la requête, mais 7 millions le sont maintenant.
Vous pouvez planifier des mises à jour plus fréquentes des statistiques, envisager des indicateurs de trace 2389 - 90
ou les utiliser OPTIMIZE FOR UKNOWN
pour éviter les suppositions plutôt que de pouvoir utiliser les statistiques actuellement trompeuses de la datetime
colonne.
Cela pourrait ne pas être nécessaire dans la prochaine version de SQL Server (après 2012). Un élément Connect associé contient la réponse intrigante
Publié par Microsoft le 28/08/2012 à 13h35
Nous avons apporté une amélioration à l'estimation de la cardinalité pour la prochaine version majeure qui résout ce problème. Restez à l'écoute pour plus de détails une fois que nos aperçus seront publiés. Eric
Cette amélioration de 2014 est examinée par Benjamin Nevarez vers la fin de l'article:
Premier aperçu du nouvel estimateur de cardinalité SQL Server .
Il semble que le nouvel estimateur de cardinalité recule et utilise la densité moyenne dans ce cas plutôt que de donner l'estimation à 1 ligne.
Quelques détails supplémentaires sur l'estimateur de cardinalité de 2014 et le problème clé croissant ici:
Nouvelle fonctionnalité dans SQL Server 2014 - Partie 2 - Nouvelle estimation de cardinalité