Ich habe die folgende Abfrage:
select databasename
from somedb.dbo.bigtable l where databasename ='someval' and source <>'kt'
and not exists(select 1 from dbo.smalltable c where c.source=l.source)
Die obige Abfrage ist in drei Sekunden abgeschlossen.
Wenn die obige Abfrage einen Wert zurückgibt, soll die gespeicherte Prozedur EXIT sein. Deshalb habe ich sie wie folgt umgeschrieben:
If Exists(
select databasename
from somedb.dbo.bigtable l where databasename ='someval' and source <>'kt'
and not exists(select 1 from dbo.smalltable c where c.source=l.source)
)
Begin
Raiserror('Source missing',16,1)
Return
End
Dies dauert jedoch 10 Minuten.
Ich kann die obige Abfrage wie folgt umschreiben, was auch in weniger als 3 Sekunden erledigt ist:
select databasename
from somedb.dbo.bigtable l where databasename ='someval' and source <>'kt'
and not exists(select 1 from dbo.smalltable c where c.source=l.source
if @@rowcount >0
Begin
Raiserror('Source missing',16,1)
Return
End
Das Problem bei der obigen Umschreibung ist, dass die obige Abfrage Teil einer größeren gespeicherten Prozedur ist und mehrere Ergebnismengen zurückgibt. In C # durchlaufen wir jede Ergebnismenge und führen eine Verarbeitung durch.
Das oben Gesagte gibt eine leere Ergebnismenge zurück. Wenn ich mich für diesen Ansatz entscheide, muss ich mein C # ändern und die Bereitstellung erneut durchführen.
Also meine Frage ist,
Warum ändert die Verwendung von nur
IF EXISTS
den Plan, um so viel Zeit in Anspruch zu nehmen?
Nachfolgend finden Sie die Details, die Ihnen helfen können, und lassen Sie mich wissen, wenn Sie weitere Details benötigen:
- Erstellen Sie ein Tabellen- und Statistikskript, um denselben Plan wie meinen zu erhalten
- Zeitlupenplan
Schneller Ausführungsplan
Langsamer Plan mit Brentozar Plan einfügen
Schneller Plan mit Brentozar Plan einfügen
Hinweis: Beide Abfragen sind identisch (mithilfe von Parametern). Der einzige Unterschied besteht darin, dass EXISTS
ich bei der Anonymisierung möglicherweise einige Fehler gemacht habe.
Die Tabellenerstellungsskripts sind unten aufgeführt:
http://pastebin.com/CgSHeqXc - kleine Tabellenstatistik
http://pastebin.com/GUu9KfpS - große Tabellenstatistik
quelle
Antworten:
Wie von erklärt worden Paul White in seinem Blog - Post: Inside the Optimizer: Row Tore In Tiefe die
EXISTS
eine Reihe Ziel einleitet, die bevorzugtNESTED LOOPS
oderMERGE JOIN
überHASH MATCH
In Ihrer Abfrage führt dies anscheinend zu verschachtelten Schleifen und zum Entfernen der Parallelität, was zu einem langsameren Plan führt.
Daher müssten Sie wahrscheinlich eine Möglichkeit finden, Ihre Abfrage umzuschreiben, ohne die
NOT EXISTS
von Ihrer Abfrage zu verwenden.Möglicherweise müssen Sie Ihre Abfrage mit a neu schreiben
LEFT OUTER JOIN
und überprüfen, ob in smalltable keine Zeile vorhanden ist, indem Sie auf testenNULL
Sie könnten wahrscheinlich auch eine
EXCEPT
Abfrage verwenden, abhängig von der Anzahl der zu vergleichenden Felder:Wohlgemerkt , Aaron Bertrand hat einen Blog-Beitrag , der Gründe dafür liefert, warum er NOT EXISTS vorzieht, die Sie durchlesen sollten, um zu sehen, ob andere Ansätze besser funktionieren, und um sich der potenziellen Korrektheitsprobleme im Fall von NULL-Werten bewusst zu sein.
Verwandte Fragen und Antworten : WENN EXISTS länger dauert als die eingebettete select-Anweisung
quelle
Sie müssen Ihre Abfrage unter Verwendung expliziter Verknüpfungen neu schreiben und angeben, welche Verknüpfungsoperation Sie verwenden möchten (Schleife, Hash oder Zusammenführung).
Bei Verwendung von EXISTS oder NOT EXISTS generierte der SQL Server einen Abfrageplan mit NESTED LOOP-Operation, wobei davon ausgegangen wurde, dass alle Zeilen in der Gruppe nacheinander durchsucht werden und nach der ersten Zeile gesucht wird, um die Bedingung zu erfüllen. Mit HASH JOIN wird es beschleunigt.
quelle
Ich bin auf dasselbe Problem gestoßen. Ich habe es geschafft, mich durch Vermeiden der Verwendung von "EXISTS" und durch Verwenden der Funktion "COUNT ()" und der Anweisung "IF ... ELSE" abzuarbeiten.
Versuchen Sie für Ihr Beispiel Folgendes:
Der Grund, warum ich "+ 1" zur Zählung hinzufüge, ist, dass ich "> 1" in der IF-Bedingung verwenden kann. Wenn "> 0" oder "<> 0" verwendet wird, wird die Abfrage ausgelöst, um verschachtelte Schleifen anstelle von HASH zu verwenden Spiel. Ich habe nicht untersucht, warum genau das passiert. Es wäre interessant herauszufinden, warum.
Ich hoffe, das hilft!
quelle