Ok, ich habe eine Abfrage für nicht gespeicherte Prozeduren, die wir in einem SSRS-Bericht verwenden. Diese Abfrage war höllisch langsam (ich hatte die ursprüngliche Version dieser Abfrage in den letzten zwei Stunden ausgeführt, immer noch nicht ausgeführt). Um sie zu verbessern, habe ich sie von Grund auf neu geschrieben und mir Folgendes ausgedacht:
Hier ist der langweilige Teil des Wortproblems:
Wir wollen eine Liste der ziehen TOP 5
Kunden pro Vertriebsmitarbeiter, aber ausschließen die TOP 10
Gesamt Kunden aus dieser Liste. (Wenn John Doe also Clients A, B, C, D und E hat und Client C zu den Top 10 gehört, ziehen Sie nur A, B, D und E.)
Um dies zu tun, verwendete die erste Abfrage ein IN (... NOT IN ( ) )
, also dachte ich, dass das Verschachteln von IN
das Problem war, um es umzuschreiben, tat ich ein OUTER APPLY
, das wirklich alles kaputt machte.
Wie auch immer, ich habe das alles behoben und die Abfrage ausgeführt, und es dauerte immer noch 10-15 Sekunden, von denen ich annahm, dass es sich um Parameter-Sniffing handelte. Zur Untersuchung habe ich die Abfrage in SSMS ausgeführt, hinzugefügt OPTION (RECOMPILE)
(um zu sehen, welcher Abfrageplan generiert wird) und Folgendes erhalten:
Es kann hier auf Brent Ozars 'Paste The Plan' angesehen werden . Die Abfrage, die dies generiert hat, war:
DECLARE @Top10Temp TABLE (Id INT)
INSERT INTO @Top10Temp
SELECT TOP 10 Id
FROM Object1
WHERE Column2 = @ReportId
AND Column3 = 0
GROUP BY Id
ORDER BY SUM(Column4 + Column5) DESC
SELECT Object2.*
FROM Object1 AS Object2
OUTER APPLY (
SELECT TOP 5
Object3.Id,
SUM(Object3.Column4 + Object3.Column5) AS Column6
FROM Object1 AS Object3
WHERE Object3.Column3 = 0
AND Object3.Column7 = Object2.Column7
AND Object3.Column2 = @ReportId
GROUP BY
Object3.Id
ORDER BY
SUM(Object3.Column4 + Object3.Column5) DESC
) AS Object4
WHERE Object2.Column2 = @ReportId
AND Object2.Column3 = 0
AND Object2.Id = Object4.Id
AND Object2.Id NOT IN (SELECT Id FROM @Top10Temp)
ORDER BY Object2.Column7
OPTION (RECOMPILE)
Jetzt hat die gleiche Abfrage aber mit OPTION (OPTIMIZE FOR UNKNOWN)
folgendem Plan generiert:
Was auch unter "Plan einfügen" angezeigt werden kann . Dieser Plan wurde in weniger als 1 Sekunde ausgeführt.
Wenn ich hinzufüge OPTION (OPTIMIZE FOR (@ReportId = #))
, wobei #
das gleiche wie die @ReportId
Variable ist, erhalte ich den gleichen Abfrageplan wie das zweite.
Habe ich etwas falsch gemacht? Ich habe Probleme zu verstehen, was passiert ist, daher werden alle Informationen sehr geschätzt. (Ich mag es auch nicht, den Optimierer über Hinweise zu beeinflussen, aber wenn es nötig ist, werde ich es behalten.)
quelle
Antworten:
"Um das zu untersuchen, habe ich die Abfrage in SSMS ausgeführt ..." Das ist das Problem. Lokale Variablen verwenden den Dichtevektor von Statistiken, der eine viel bessere Zeilenschätzung ergibt und folglich bereits für UNBEKANNT OPTIMIERT. Parametrisiertes dynamisches SQL verwendet das Histogramm, das die gesamte Zeilenanzahl für einen bestimmten Abschnitt abruft.
Sehen Sie sich die geschätzte Anzahl und die tatsächliche Anzahl der Zeilen für jeden Ihrer Links zum Einfügen des Plans an. Der zweite Link hat waaaayyyy bessere Schätzungen als der erste.
Ich würde Ihre SSRS-Abfrage auf einer Entwicklungsinstanz bereitstellen und einige Tests ausführen, da ich vermute, dass Sie Leistungsprobleme haben könnten.
Übrigens, aktualisieren Sie Statistiken oder erstellen Sie Indizes für diese Bestientabellen neu, wenn Sie können.
Links: Innerhalb des Statistikhistogramms und des Dichtevektors
quelle
Der langsame Plan weist eine schlechte Kardinalitätsschätzung auf, die aus der Indexsuche an Knoten 4 hervorgeht. Die geschätzte Anzahl von Zeilen beträgt 1, aber die tatsächliche Anzahl von Zeilen beträgt 3261. Hier ist das Suchprädikat:
Sie filtern nach zwei verschiedenen Spalten aus derselben Tabelle. Häufig verfügt SQL Server nicht über genügend Informationen, um eine genaue Schätzung für dieses Szenario abzugeben. Daher werden Modellierungsannahmen getroffen, die von Ihrer CE-Version, Patches, Ablaufverfolgungsflags usw. abhängen. Beispielsweise könnte angenommen werden, dass die Spalten keine Korrelation aufweisen, und die Selektivitäten miteinander multipliziert werden. Dies kann zu einer geringen Schätzung führen, wenn eine Korrelation zwischen den Filtern besteht.
Im Allgemeinen würde ich sagen, wenn Sie eine gute Leistung mit einer schlechten Schätzung erzielen, haben Sie wahrscheinlich Glück und Ihr Glück kann irgendwann ausgehen. Ich würde versuchen, diese Schätzung zu korrigieren. Ich kann Ihnen keine genauen Anweisungen geben, da zu viele Informationen fehlen (Sie können einige der fehlenden Informationen aufgrund von IP-Bedenken nicht weitergeben), aber ich kann sagen, dass eine mehrspaltige Statistik oder ein mehrspaltiger Index hilfreich sein könnte. Das Speichern der Primärschlüssel der Tabelle nach dem Filtern in eine temporäre Tabelle sollte immer funktionieren. Mit einer genaueren Schätzung würde ich einen Abfrageplan erwarten, der dem schnellen Plan ähnlich ist.
Sie haben nichts falsch gemacht, indem Sie den
OPTION (RECOMPILE)
Hinweis hinzugefügt haben . Möglicherweise haben Sie nur durch Pech eine schlechte Leistung erzielt. Die Optimierung der Parametereinbettung hilft normalerweise, anstatt Probleme zu verursachen.OPTIMIZE FOR UNKNOWN
bewirkt, dass SQL Server die Statistikobjekte anders verwendet, und es kommt nur so vor, dass Sie bei der Verwendung eine Schätzung erhalten, die der Realität näher kommt.Ich würde nicht
OPTIMIZE FOR UNKNOWN
als langfristige Lösung verwenden. Der Abfrageplan ändert sich nicht, je nachdem,@ReportId
welcher Wert Probleme verursachen kann, wenn Sie den Wert der Variablen ändern. Es ist auch eine indirekte Lösung, und Sie haben zugegeben, dass Sie nicht verstehen, wie es funktioniert. Es wäre besser, das Problem direkter anzugreifen, indem Sie die Kardinalitätsschätzung festlegen oder Zwischenergebnisse strategisch in temporären Tabellen materialisieren. In der Regel sollten Sie die Verwendung von Tabellenvariablen vermeiden, da diese keine Statistiken enthalten. Tabellenvariablen haben nur sehr begrenzte Anwendungsfälle, und ich empfehle Ihnen, sie nur zu verwenden, wenn Sie keine andere Wahl haben.quelle
@ReportId
die gleiche Anzahl von Ergebnissen hat? (Sie sollten innerhalb von 5 bis 20 Reihen voneinander liegen.) Ich weiß nicht, ob dies meiner Situation helfen würde oder nicht oder ob es überhaupt möglich ist, aber das ist eine Sache, die ich als Auswirkung sehen konnte.