Die Abfrage lautet
SELECT SUM(Amount) AS SummaryTotal
FROM PDetail WITH(NOLOCK)
WHERE ClientID = @merchid
AND PostedDate BETWEEN @datebegin AND @dateend
Die Tabelle enthält 103.129.000 Zeilen.
Der schnelle Plan wird von ClientId mit einem verbleibenden Prädikat für das Datum nachgeschlagen, es müssen jedoch 96 Suchvorgänge ausgeführt werden, um den abzurufen Amount
. Der <ParameterList>
Abschnitt im Plan ist wie folgt.
<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>
Der langsame Plan sucht nach Datum und verfügt über Suchvorgänge, um das verbleibende Vergleichselement für ClientId auszuwerten und den Betrag abzurufen (Estimated 1 vs Actual 7.388.383). Der <ParameterList>
Abschnitt ist
<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>
In diesem zweiten Fall ParameterCompiledValue
ist der nicht leer. SQL Server hat die in der Abfrage verwendeten Werte erfolgreich abgerufen.
Das Buch "Praktische Fehlerbehebung in SQL Server 2005" enthält Informationen zur Verwendung lokaler Variablen
Die Verwendung lokaler Variablen, um das Parameter-Sniffing zu unterbinden, ist ein ziemlich häufiger Trick, aber die OPTION (RECOMPILE)
und OPTION (OPTIMIZE FOR)
-Hinweise ... sind im Allgemeinen eleganter und etwas weniger riskant
Hinweis
In SQL Server 2005 ermöglicht die Kompilierung auf Anweisungsebene die Kompilierung einer einzelnen Anweisung in einer gespeicherten Prozedur, die bis kurz vor der ersten Ausführung der Abfrage zurückgestellt wird. Bis dahin wäre der Wert der lokalen Variablen bekannt. Theoretisch könnte SQL Server dies nutzen, um lokale Variablenwerte auf die gleiche Weise zu erfassen, wie Parameter erfasst werden. Da jedoch in SQL Server 7.0 und SQL Server 2000+ häufig lokale Variablen verwendet wurden, um das Parameter-Sniffing zu umgehen, wurde das Sniffing lokaler Variablen in SQL Server 2005 nicht aktiviert. Möglicherweise wird es in einer zukünftigen SQL Server-Version aktiviert, was jedoch von Vorteil ist Grund, eine der anderen in diesem Kapitel beschriebenen Optionen zu verwenden, wenn Sie die Wahl haben.
Nach einem kurzen Test ist das oben beschriebene Verhalten in 2008 und 2012 immer noch dasselbe und Variablen werden nicht für ein verzögertes Kompilieren abgehört, sondern nur, wenn ein expliziter OPTION RECOMPILE
Hinweis verwendet wird.
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
Trotz verzögerter Kompilierung wird die Variable nicht abgehört und die geschätzte Zeilenanzahl ist ungenau
Ich gehe also davon aus, dass sich der langsame Plan auf eine parametrisierte Version der Abfrage bezieht.
Das ParameterCompiledValue
ist gleich ParameterRuntimeValue
für alle Parameter , so ist dies nicht typisch Parameter Schnüffeln (wo der Plan für einen Satz von Werten zusammengestellt wurde dann für einen weiteren Satz von Werten ausgeführt werden ).
Das Problem ist, dass der Plan, der für die richtigen Parameterwerte kompiliert wurde, nicht geeignet ist.
Wahrscheinlich haben Sie das Problem mit aufsteigenden Daten, die hier und hier beschrieben werden . Für eine Tabelle mit 100 Millionen Zeilen müssen Sie 20 Millionen Zeilen einfügen (oder anderweitig ändern), bevor SQL Server die Statistiken automatisch für Sie aktualisiert. Es scheint, dass bei der letzten Aktualisierung keine Zeilen mit dem Datumsbereich in der Abfrage übereinstimmen, jetzt jedoch 7 Millionen.
Sie können häufiger Statistikaktualisierungen einplanen, Ablaufverfolgungsflags in Betracht ziehen 2389 - 90
oder verwenden, OPTIMIZE FOR UKNOWN
um nur auf Vermutungen zurückzugreifen, anstatt die derzeit irreführenden Statistiken für die datetime
Spalte verwenden zu können.
Dies ist in der nächsten Version von SQL Server (nach 2012) möglicherweise nicht erforderlich. Ein verwandtes Connect-Objekt enthält die faszinierende Antwort
Gepostet von Microsoft am 28.08.2012 um 13:35 Uhr
Wir haben eine Verbesserung der Kardinalitätsschätzung für die nächste Hauptversion vorgenommen, die dies im Wesentlichen behebt. Wenn unsere Vorschaubilder erscheinen, bleiben Sie auf dem Laufenden. Eric
Diese Verbesserung von 2014 wird von Benjamin Nevarez gegen Ende des Artikels betrachtet:
Ein erster Blick auf den New SQL Server Cardinality Estimator .
Es scheint, dass der neue Kardinalitätsschätzer zurückgreift und in diesem Fall die durchschnittliche Dichte verwendet, anstatt die 1-Zeilen-Schätzung anzugeben.
Einige zusätzliche Details zum Kardinalitätsschätzer 2014 und dem aufsteigenden Schlüsselproblem hier:
Neue Funktionen in SQL Server 2014 - Teil 2 - Neue Kardinalitätsschätzung
Ich hatte genau das gleiche Problem, bei dem eine gespeicherte Prozedur langsam wurde
OPTIMIZE FOR UNKNOWN
undRECOMPILE
Abfragetipps die Langsamkeit lösten und die Ausführungszeit beschleunigten. Die folgenden beiden Methoden hatten jedoch keinen Einfluss auf die Langsamkeit der gespeicherten Prozedur: (i) Löschen des Caches (ii) Verwenden von WITH RECOMPILE. Also, wie Sie sagten, war es nicht wirklich Parameter schnüffeln.Die Trace-Flags 2389 und 2390 haben auch nicht geholfen. Nur die Aktualisierung der Statistiken (
EXEC sp_updatestats
) hat es für mich getan.quelle