Bei SourceTable
mehr als 15 Millionen Einträgen und Bad_Phrase
mehr als 3.000 Einträgen dauert die Ausführung der folgenden Abfrage unter SQL Server 2005 SP4 fast 10 Stunden.
UPDATE [SourceTable]
SET
Bad_Count=
(
SELECT
COUNT(*)
FROM Bad_Phrase
WHERE
[SourceTable].Name like '%'+Bad_Phrase.PHRASE+'%'
)
In Englisch zählt diese Abfrage die Anzahl der in Bad_Phrase aufgelisteten unterschiedlichen Phrasen, die eine Teilzeichenfolge des Felds Name
in sind, SourceTable
und platziert dieses Ergebnis dann in das Feld Bad_Count
.
Ich hätte gerne einige Vorschläge, wie diese Abfrage erheblich schneller ausgeführt werden kann.
sql-server
sql-server-2005
update
subquery
Matthew Lehrer
quelle
quelle
Antworten:
Obwohl ich mit anderen Kommentatoren übereinstimme, dass dies ein rechenintensives Problem ist, denke ich, dass es viel Raum für Verbesserungen gibt, wenn Sie die von Ihnen verwendete SQL optimieren. Zur Veranschaulichung erstelle ich einen gefälschten Datensatz mit 15-MM-Namen und 3-KB-Ausdrücken, führe den alten Ansatz aus und führe einen neuen Ansatz aus.
Vollständiges Skript zum Generieren eines gefälschten Datensatzes und Testen des neuen Ansatzes
TL; DR
Auf meinem Computer und diesem gefälschten Datensatz dauert der ursprüngliche Ansatz ca. 4 Stunden . Der vorgeschlagene neue Ansatz dauert ungefähr 10 Minuten , was eine erhebliche Verbesserung darstellt. Hier ist eine kurze Zusammenfassung des vorgeschlagenen Ansatzes:
Ursprünglicher Ansatz: algorithmische Analyse
Aus dem Plan der ursprünglichen
UPDATE
Aussage können wir ersehen, dass der Arbeitsaufwand sowohl zur Anzahl der Namen (15MM) als auch zur Anzahl der Phrasen (3K) linear proportional ist. Wenn wir also sowohl die Anzahl der Namen als auch die der Phrasen mit 10 multiplizieren, wird die Gesamtlaufzeit ~ 100-mal langsamer sein.Die Abfrage ist tatsächlich proportional zur Länge der
name
; Dies ist zwar im Abfrageplan etwas versteckt, kommt aber in der "Anzahl der Ausführungen" zum Suchen in der Tabellenspule durch. Im aktuellen Plan können wir sehen, dass dies nicht nur einmal proname
, sondern tatsächlich einmal pro Zeichen im Offset auftrittname
. Dieser Ansatz hat also eine Laufzeitkomplexität von 0 (# names
*# phrases
*name length
).Neuer Ansatz: Code
Dieser Code ist auch im vollständigen Pastebin verfügbar , ich habe ihn jedoch aus Gründen der Benutzerfreundlichkeit hierher kopiert. Der Pastebin verfügt auch über die vollständige Prozedurdefinition, die die unten angezeigten Variablen
@minId
und enthält@maxId
, um die Grenzen des aktuellen Stapels zu definieren.Neuer Ansatz: Abfragepläne
Zuerst generieren wir die Teilzeichenfolge beginnend mit jedem Zeichenversatz
Erstellen Sie dann einen Clustered-Index für diese Teilzeichenfolgen
Nun suchen wir für jede fehlerhafte Phrase in diesen Teilzeichenfolgen nach Übereinstimmungen. Wir berechnen dann die Anzahl der eindeutigen fehlerhaften Phrasen, die mit einem oder mehreren Teilstrings dieser Zeichenfolge übereinstimmen. Dies ist wirklich der Schlüsselschritt; Aufgrund der Art und Weise, wie wir die Teilzeichenfolgen indiziert haben, müssen wir nicht länger ein vollständiges Kreuzprodukt aus fehlerhaften Phrasen und Namen prüfen. Dieser Schritt, der die eigentliche Berechnung durchführt, macht nur etwa 10% der tatsächlichen Laufzeit aus (der Rest ist die Vorverarbeitung von Teilzeichenfolgen).
Zuletzt führen Sie die eigentliche Update-Anweisung aus, indem Sie a verwenden
LEFT OUTER JOIN
, um allen Namen, für die wir keine fehlerhaften Phrasen gefunden haben, eine Anzahl von 0 zuzuweisen.Neuer Ansatz: Algorithmische Analyse
Der neue Ansatz kann in zwei Phasen unterteilt werden: Vorverarbeitung und Matching. Definieren wir die folgenden Variablen:
N
= Anzahl der NamenB
= Anzahl der schlechten PhrasenL
= durchschnittliche Länge des Namens in ZeichenDie Vorverarbeitung Phase
O(N*L * LOG(N*L))
zu schaffen , umN*L
Teilstrings und dann sortieren.Die tatsächliche Übereinstimmung dient
O(B * LOG(N*L))
dazu, nach jeder fehlerhaften Phrase in den Teilzeichenfolgen zu suchen.Auf diese Weise haben wir einen Algorithmus erstellt, der nicht linear mit der Anzahl der fehlerhaften Phrasen skaliert. Dies ist eine wichtige Voraussetzung für die Leistungssteigerung, wenn wir auf 3K-Phrasen und mehr skalieren. Anders gesagt, die ursprüngliche Implementierung dauert ungefähr das 10-fache, solange wir von 300 schlechten Phrasen zu 3K schlechten Phrasen übergehen. In ähnlicher Weise würde es weitere 10x so lange dauern, wenn wir von 3K schlechten Phrasen auf 30K wechseln würden. Die neue Implementierung wird jedoch sublinear skaliert und benötigt weniger als das Zweifache der Zeit, die bei 3K-fehlerhaften Phrasen gemessen wird, wenn sie auf 30K-fehlerhafte Phrasen skaliert wird.
Annahmen / Vorbehalte
SORT
die Zeichenfolgen auf den Teilzeichenfolgen für jeden Stapel unabhängig sind und problemlos in den Speicher passen. Sie können die Stapelgröße nach Bedarf ändern, es ist jedoch nicht ratsam, alle 15-MM-Zeilen in einem Stapel zu testen.Verwandte Blog-Post
Aaron Bertrand untersucht diese Art von Lösung ausführlicher in seinem Blogbeitrag. Eine Möglichkeit, einen Index nach einem führenden Platzhalter zu durchsuchen .
quelle
Lassen Sie uns das offensichtliche Problem, das Aaron Bertrand in den Kommentaren angesprochen hat, für eine Sekunde beiseite legen :
Die Tatsache, dass Ihre Unterabfrage die Platzhalter auf beiden Seiten verwendet, wirkt sich dramatisch auf die Sargabilität aus . So nehmen Sie ein Zitat aus diesem Blogeintrag entgegen:
Tauschen Sie das Wort "nut" für jedes "bad word" und "Product" für aus
SourceTable
und kombinieren Sie dies mit Aarons Kommentar. Sie sollten verstehen, warum es extrem schwierig ist (unmöglich zu lesen), es mit Ihrem aktuellen Algorithmus schnell laufen zu lassen.Ich sehe ein paar Möglichkeiten:
Abhängig von Ihren Anforderungen würde ich entweder Option 3 oder 4 empfehlen.
quelle
das ist erstmal nur ein seltsames update
Wie '%' + [Bad_Phrase]. [PHRASE] bringt Sie um
Das kann keinen Index verwenden
Das
Datendesign ist nicht optimal für die Geschwindigkeit. Können Sie die [Bad_Phrase]. [PHRASE] in einzelne Phrasen / Wörter aufteilen?
Wenn die gleiche Phrase / Wort mehr als eine erscheint , kann man es mehr geben als einmal , wenn Sie es wollen , eine höhere Zählung haben
also die Anzahl der Zeilen in einem schlechten pharase gehen würde ,
wenn Sie können , dann wird dies viel viel schneller
Nicht sicher, ob 2005 es unterstützt, aber Volltextindex und Verwendung enthält
quelle