Kurz gesagt, wir aktualisieren kleine Personentabellen mit Werten aus einer sehr großen Personentabelle. In einem kürzlich durchgeführten Test dauert die Ausführung dieses Updates ca. 5 Minuten.
Wir sind auf die scheinbar albernste Optimierung gestoßen, die scheinbar perfekt funktioniert! Dieselbe Abfrage wird jetzt in weniger als 2 Minuten ausgeführt und führt perfekt zu denselben Ergebnissen.
Hier ist die Abfrage. Die letzte Zeile wird als "Optimierung" hinzugefügt. Warum die intensive Verkürzung der Abfragezeit? Vermissen wir etwas? Könnte dies in Zukunft zu Problemen führen?
UPDATE smallTbl
SET smallTbl.importantValue = largeTbl.importantValue
FROM smallTableOfPeople smallTbl
JOIN largeTableOfPeople largeTbl
ON largeTbl.birth_date = smallTbl.birthDate
AND DIFFERENCE(TRIM(smallTbl.last_name),TRIM(largeTbl.last_name)) = 4
AND DIFFERENCE(TRIM(smallTbl.first_name),TRIM(largeTbl.first_name)) = 4
WHERE smallTbl.importantValue IS NULL
-- The following line is "the optimization"
AND LEFT(TRIM(largeTbl.last_name), 1) IN ('a','à','á','b','c','d','e','è','é','f','g','h','i','j','k','l','m','n','o','ô','ö','p','q','r','s','t','u','ü','v','w','x','y','z','æ','ä','ø','å')
Technische Hinweise: Wir sind uns bewusst, dass die Liste der zu testenden Buchstaben möglicherweise einige weitere Buchstaben benötigt. Wir sind uns auch der offensichtlichen Fehlerquote bei der Verwendung von "DIFFERENCE" bewusst.
Abfrageplan (regulär): https://www.brentozar.com/pastetheplan/?id=rypV84y7V
Abfrageplan (mit "Optimierung"): https://www.brentozar.com/pastetheplan/?id=r1aC2my7E
AND LEFT(TRIM(largeTbl.last_name), 1) BETWEEN 'a' AND 'z' COLLATE LATIN1_GENERAL_CI_AI
sollte tun, was Sie dort wollen, ohne dass Sie alle Zeichen auflisten müssen und Code haben, der schwer zu lesen istWHERE
falsch ist? Beachten Sie insbesondere, dass beim Vergleich zwischen Groß- und Kleinschreibung unterschieden werden kann.Latin1_General_100_CI_AI
. Für SQL Server 2012 und höher (mindestens bis SQL Server 2019) ist es am besten, die für zusätzliche Zeichen aktivierten Kollatierungen in der höchsten Version für das verwendete Gebietsschema zu verwenden. Das wäre alsoLatin1_General_100_CI_AI_SC
in diesem Fall. Versionen> 100 (bisher nur Japanisch) haben (oder brauchen) nicht_SC
(zBJapanese_XJIS_140_CI_AI
).Antworten:
Dies hängt von den Daten in Ihren Tabellen, Ihren Indizes usw. ab. Schwer zu sagen, ohne die Ausführungspläne / die io + -Zeitstatistik vergleichen zu können.
Der Unterschied, den ich erwarten würde, ist die zusätzliche Filterung vor dem JOIN zwischen den beiden Tabellen. In meinem Beispiel habe ich die Aktualisierungen in Auswahl geändert, um meine Tabellen wiederzuverwenden.
Der Ausführungsplan mit "der Optimierung"
Ausführungsplan
Sie sehen deutlich, dass ein Filtervorgang stattfindet. In meinen Testdaten wurden keine Datensätze herausgefiltert und daher wurden keine Verbesserungen vorgenommen.
Der Ausführungsplan ohne "die Optimierung"
Ausführungsplan
Der Filter ist weg, was bedeutet, dass wir uns auf den Join verlassen müssen, um nicht benötigte Datensätze herauszufiltern.
Andere Gründe Ein weiterer Grund / eine weitere Konsequenz für die Änderung der Abfrage könnte sein, dass beim Ändern der Abfrage ein neuer Ausführungsplan erstellt wurde, der zufällig schneller ist. Ein Beispiel hierfür ist die Engine, die einen anderen Join-Operator auswählt, aber das ist an dieser Stelle nur eine Vermutung.
BEARBEITEN:
Klarstellung nach Erhalt der beiden Abfragepläne:
Die Abfrage liest 550 Millionen Zeilen aus der großen Tabelle und filtert sie heraus.
Dies bedeutet, dass das Prädikat das meiste der Filterung ausführt, nicht das Suchprädikat. Dies führt dazu, dass die Daten gelesen, aber viel weniger zurückgegeben werden.
Wenn Sie SQL Server dazu bringen, einen anderen Index (Abfrageplan) zu verwenden, oder einen Index hinzufügen, kann dies behoben werden.
Warum hat die Optimierungsabfrage nicht dasselbe Problem?
Da ein anderer Abfrageplan verwendet wird, mit einem Scan anstelle einer Suche.
Ohne irgendwelche Suchvorgänge durchzuführen, sondern nur 4 Millionen Zeilen zurückzugeben, um damit zu arbeiten.
Nächster Unterschied
Ohne Berücksichtigung des Aktualisierungsunterschieds (bei der optimierten Abfrage wird nichts aktualisiert) wird für die optimierte Abfrage eine Hash-Übereinstimmung verwendet:
Anstelle eines verschachtelten Loop-Joins auf dem nicht optimierten:
Eine verschachtelte Schleife ist am besten, wenn eine Tabelle klein und die andere groß ist. Da beide nahe an der gleichen Größe liegen, würde ich argumentieren, dass das Hash-Match in diesem Fall die bessere Wahl ist.
Überblick
Die optimierte Abfrage
Der Plan der optimierten Abfrage weist Parallellismus auf, verwendet einen Hash-Match-Join und muss weniger Rest-E / A-Filterung durchführen. Außerdem wird eine Bitmap verwendet, um Schlüsselwerte zu entfernen, die keine Verknüpfungszeilen erzeugen können. (Auch wird nichts aktualisiert)
Die nicht optimierte Abfrage Der Plan der nicht optimierten Abfrage weist keine Parallellismus auf, verwendet eine verschachtelte Schleifenverknüpfung und muss eine Rest-E / A-Filterung für 550 Millionen Datensätze durchführen. (Auch das Update findet statt)
Was können Sie tun, um die nicht optimierte Abfrage zu verbessern?
Ändern des Index in Vorname und Nachname in der Liste der Schlüsselspalten:
CREATE INDEX IX_largeTableOfPeople_birth_date_first_name_last_name auf dbo.largeTableOfPeople (Geburtsdatum, Vorname, Nachname) include (id)
Aufgrund der Verwendung von Funktionen und der Größe dieser Tabelle ist dies möglicherweise nicht die optimale Lösung.
(HASH JOIN, MERGE JOIN)
zur AbfrageTestdaten + Abfragen verwendet
quelle
Es ist nicht klar, dass die zweite Abfrage tatsächlich eine Verbesserung darstellt.
Die Ausführungspläne enthalten QueryTimeStats, die einen viel weniger dramatischen Unterschied aufweisen als in der Frage angegeben.
Der langsame Plan hatte eine verstrichene Zeit von
257,556 ms
(4 Minuten 17 Sekunden). Der schnelle Plan hatte eine verstrichene Zeit von190,992 ms
(3 Minuten, 11 Sekunden), obwohl er mit einem Parallelitätsgrad von 3 lief.Darüber hinaus wurde der zweite Plan in einer Datenbank ausgeführt, in der nach dem Join keine Arbeit mehr zu erledigen war.
Erster Plan
Zweiter Plan
Diese zusätzliche Zeit könnte durchaus durch die Arbeit erklärt werden, die zum Aktualisieren von 3,5 Millionen Zeilen erforderlich ist (die Arbeit, die der Aktualisierungsoperator benötigt, um diese Zeilen zu lokalisieren, die Seite zu verriegeln, die Aktualisierung auf die Seite zu schreiben und das Transaktionsprotokoll ist nicht vernachlässigbar).
Wenn dies beim Vergleich von Gleichem mit Gleichem tatsächlich reproduzierbar ist, ist die Erklärung, dass Sie in diesem Fall gerade Glück gehabt haben.
Der Filter mit den 37
IN
Bedingungen eliminierte nur 51 Zeilen von den 4.008.334 in der Tabelle, aber der Optimierer ging davon aus, dass er viel mehr eliminieren würdeSolche falschen Kardinalitätsschätzungen sind normalerweise eine schlechte Sache. In diesem Fall wurde ein anders geformter (und paralleler) Plan erstellt, der anscheinend (?) Besser für Sie funktionierte, trotz der durch die massive Unterschätzung verursachten Hash-Verschüttungen.
Ohne
TRIM
SQL Server ist es möglich, dies in ein Bereichsintervall im Basisspaltenhistogramm zu konvertieren und viel genauere Schätzungen zu liefern, aber mit demTRIM
wird nur auf Vermutungen zurückgegriffen.Die Art der Vermutung kann variieren, aber die Schätzung für ein einzelnes Prädikat
LEFT(TRIM(largeTbl.last_name), 1)
wird unter bestimmten Umständen * nur geschätzttable_cardinality/estimated_number_of_distinct_column_values
.Ich bin mir nicht sicher, unter welchen Umständen - die Datengröße scheint eine Rolle zu spielen. Ich konnte dies wie hier mit Datentypen mit großer fester Länge reproduzieren , bekam aber eine andere, höhere
varchar
Schätzung mit (die nur eine flache Schätzung von 10% und geschätzte 100.000 Zeilen verwendete). @Solomon Rutzky weist darauf hin, dass, wenn dasvarchar(100)
mit Leerzeichen aufgefüllt ist, wie es fürchar
die untere Schätzung geschieht, verwendet wirdDie
IN
Liste wird erweitertOR
und SQL Server verwendet exponentielles Backoff mit maximal 4 berücksichtigten Prädikaten. Die219.707
Schätzung ergibt sich also wie folgt.quelle