unter Verwendung von SQL Server 2014; ((SP1-CU3) (KB3094221) 10. Oktober 2015 x64
Ich habe die folgende Abfrage
%Vor%Es gibt ~ 70k, 35k und 73k Zeilen in t1, t2 bzw. t3.
Wenn ich die order by
weglasse, wird diese Abfrage in 3 Sekunden mit 73k Zeilen ausgeführt.
Wie geschrieben, dauerte die Abfrage 8,5 Minuten , um ~ 50k Zeilen zurückzugeben (ich habe sie gestoppt)
Das Umschalten der Reihenfolge von LEFT JOIN
s macht einen Unterschied:
Dies läuft jetzt in 3 Sekunden.
Ich habe keine Indizes für die Tabellen. Das Hinzufügen der Indizes t1.tradeNo
und t2.trade_id
und t3.TradeReportID
hat keine Auswirkungen.
Das Ausführen der Abfrage mit nur einem linken Join (beide Szenarien) in Kombination mit order by
ist schnell.
Es ist in Ordnung für mich, die Reihenfolge der LEFT JOIN
s zu vertauschen, aber das erklärt nicht viel warum das passiert und unter welchen Umständen es wieder passieren kann
Der geschätzte Ausführungsplan ist: (langsam)
VS
Umschalten der Reihenfolge der linken Joins (schnell):
was ich bemerke, sind deutlich anders, aber ich kann diese nicht interpretieren, um den Leistungsunterschied zu erklären
Es scheint, dass das Hinzufügen der order by
-Klausel zu dem Ausführungsplan führt, der den Tabellen-Spool (Lazy Spool) verwendet, anstatt dies in der schnellen Abfrage zu verwenden.
Wenn ich die Tabelle spool über DBCC RULEOFF ('BuildSpool');
ausschalte, behebt dies die Geschwindigkeit, aber laut Dieser Beitrag wird nicht empfohlen und kann dies auch nicht pro Abfrage tun
Eine der zurückgegebenen Spalten ( table3.Text
) hat den Typ varchar(max)
) - Wenn dies in nvarchar(512)
geändert wird, ist die ursprüngliche (langsame) Abfrage jetzt schnell - dh der Ausführungsplan entscheidet sich nun, die Tabelle nicht zu verwenden Spool - Beachten Sie auch, dass auch wenn der Typ varchar(max)
ist, die Feldwerte für jede einzelne Zeile NULL sind. Das ist jetzt fixierbar, aber ich bin keiner der Klügeren.
Warnungen im Ausführungsplan angegeben
Die Typkonvertierung im Ausdruck (CONVERT_IMPLICIT (nvarchar (50), [t2]. [trade_id], 0)) kann "CardinalityEstimate" in der Abfrageplanauswahl beeinflussen, ...
t1.tradeNo
ist nvarchar(21)
- die anderen beiden sind varchar(50)
- nach der Änderung der letzten beiden auf das gleiche wie das erste verschwindet das Problem! (varchar (max) wie in UPDATE 2 unverändert angegeben)
Wenn dieses Problem verschwindet, wenn entweder UPDATE 2 oder UPDATE 3 korrigiert werden, würde ich vermuten, dass es eine Kombination des Abfrageoptimierers mit einer temporären Tabelle (Tabellenspool) für eine Spalte ist, die eine unbegrenzte Größe hat - sehr interessant trotz der% Spalte "co_de%" enthält keine Daten.
Dies ist eine Klasse von Problemen, die in DBs recht häufig auftritt. Die Datenbank nimmt das falsche bezüglich der Zusammensetzung der Daten an, mit denen sie sich befassen wird. Die Kosten, die in Ihrem geschätzten Ausführungsplan angezeigt werden, sind wahrscheinlich weit von dem entfernt, was Sie in Ihrem tatsächlichen Plan erhalten würden. Wir alle machen Fehler und es wäre wirklich gut, wenn SQL Server von sich aus lernen würde, aber derzeit nicht. Es wird eine Rückkehrzeit von 2 Sekunden geschätzt, obwohl es sich immer wieder als falsch erwiesen hat. Um ehrlich zu sein, ich kenne keine DBMS, die maschinelles Lernen besser machen können.
Der schwierigste Teil wird durch Sortieren von Tabelle3 erledigt. Das bedeutet, dass es einen effizienten Merge-Join durchführen kann, was wiederum bedeutet, dass es keinen Grund gibt, beim Spoolen faul zu sein.
Wenn Sie eine ID haben, die sich auf das selbe bezieht, das als zwei verschiedene Typen und Datenlängen gespeichert ist, sollte das nie notwendig sein und wird für eine ID nie eine gute Idee sein. In diesem Fall nvarchar
an einer Stelle varchar
an einer anderen Stelle. Wenn es dadurch scheitert, eine Kardinalitätsschätzung zu erhalten, ist das der Hauptfehler und hier ist der Grund:
Der Optimierer hofft, dass nur ein paar eindeutige Zeilen von table3 benötigt werden. Nur eine Handvoll Optionen wie "Männlich", "Weiblich", "Andere". Das wäre die sogenannte "niedrige Kardinalität". Stellen Sie sich also vor, tradeNo enthält aus irgendeinem Grund tatsächlich IDs für die Geschlechter. Denken Sie daran, Sie sind mit Ihren menschlichen Fähigkeiten der Kontextualisierung vertraut, wer weiß, dass das sehr unwahrscheinlich ist. Die DB ist blind dafür. Hier ist also, was erwartet wird: Wenn die Abfrage ausgeführt wird, wenn sie zum ersten Mal auf die ID für "Männlich" stößt, wird sie die zugehörigen Daten (wie das Wort "Männlich") langsam abrufen und in die Spool-Datei einfügen. Als nächstes, weil es sortiert ist, erwartet es nur viel mehr Männer und einfach wiederverwenden, was es bereits in der Spule hat.
Im Grunde plant es, die Daten aus den Tabellen 1 und 2 in ein paar großen Blöcken zu holen, die ein- oder zweimal anhalten, um neue Details aus Tabelle 3 zu holen. In der Praxis ist das Stoppen nicht zufällig. Tatsächlich kann es sogar für jede einzelne Zeile anhalten, da es hier viele verschiedene IDs gibt. Die Lazy Spool ist wie nach oben gehen, um eine kleine Sache nach der anderen zu bekommen . Gut, wenn Sie nur Ihre Brieftasche wollen. Weniger gut, wenn Sie umziehen und der Van draußen ist.
Der wahrscheinliche Grund dafür, dass die Größe des Feldes in Tabelle 3 verkleinert wurde, war, dass es weniger einen komparativen Vorteil bei der Ausführung der Lazy Spool über eine volle Sortierung im Voraus schätzte. Mit varchar weiß es nicht, wie viele Daten es gibt, wie viel potenziell möglich ist. Je größer die potenziellen Datenblöcke sind, die gemischt werden müssen, desto mehr physische Arbeit ist erforderlich.
Machen Sie aus Ihrem Tabellenschema und den Indizes die tatsächliche Form der Daten.
varchar
in einer Tabelle sein kann, ist es sehr unwahrscheinlich, dass die zusätzlichen Zeichen in nvarchar
für eine andere Tabelle benötigt werden. Vermeiden Sie die Notwendigkeit von ID-Conversions und verwenden Sie möglichst Integer anstelle von Zeichen. Verschieben Sie mit der Join-Reihenfolge in die richtige Richtung.
Wenn alles andere fehlschlägt
INCLUDE
alle Felder enthält, die Sie in table3 im Index von TradeReportId benötigen. Der Grund, warum die Indizes nicht so viel helfen konnten, ist, dass sie es einfach machen, wie zu re-sort zu identifizieren, aber es wurde noch nicht physisch erledigt . Das ist Arbeit, die es mit einer Lazy Spool zu optimieren hoffte, aber wenn die Daten enthalten wären, wäre es bereits verfügbar, also keine Arbeit zur Optimierung. Tags und Links sql-server performance