Auftrag verlangsamt die Abfrage massiv

9

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:

%Vor%

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)

(Ausrufezeichen)

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

AKTUALISIEREN

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

AKTUALISIEREN 2

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.

AKTUALISIEREN 3

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.

    
wal 27.11.2015, 00:16
quelle

2 Antworten

4

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.

Wo Ihre Suchanfrage schnell ist

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.

Wo es langsam ist

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.

Was Sie in Zukunft vermeiden können

Machen Sie aus Ihrem Tabellenschema und den Indizes die tatsächliche Form der Daten.

  • Wenn eine ID 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.
  • Fragen Sie sich, ob für diese Tabellen TradeNo ausgefüllt werden muss alle Zeilen. Wenn dies der Fall ist, machen Sie in dieser Tabelle keine NULL-Werte. Als nächstes frage, ob die Die ID sollte für jede dieser Tabellen eindeutig sein und als solche definiert sein der entsprechende Index. Einzigartig ist die Definition von hoher Kardinalität damit es diesen Fehler nicht wieder macht.

Verschieben Sie mit der Join-Reihenfolge in die richtige Richtung.

  • Die Reihenfolge, in der Sie Ihre Joins im SQL haben, ist ein Signal an die Datenbank, wie mächtig / schwierig Sie erwarten, dass jeder Join ist. (Manchmal weiß man als Mensch mehr. Wenn man zB nach 50-jährigen Astronauten fragt, weiß man, dass das Filtern nach Astronauten der erste Filter ist, aber vielleicht mit dem Alter bei der Suche nach 50-jährigen Büroangestellten beginnt.) Das schwere Zeug sollte kommen zuerst. Es wird Sie ignorieren, wenn es denkt, dass es die Informationen hat, um es besser zu wissen, aber in diesem Fall verlässt es sich auf Ihr Wissen.

Wenn alles andere fehlschlägt

  • Eine mögliche Lösung wäre, dass 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.
Adamantish 12.01.2016, 03:19
quelle
-1

Indizes für eine Tabelle sind der Schlüssel, um den Abruf von Daten zu beschleunigen. Beginnen Sie damit und versuchen Sie dann erneut, die Abfrage zu überprüfen, um zu sehen, ob die Geschwindigkeit mit 'ORDER BY'

verbessert wurde     
Mike Cave 02.12.2015 16:32
quelle

Tags und Links