Das Umbrechen von Abfragen in IF EXISTS macht es sehr langsam


16

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 EXISTSden 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:

  1. Erstellen Sie ein Tabellen- und Statistikskript, um denselben Plan wie meinen zu erhalten
  2. Zeitlupenplan
  3. 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 EXISTSich 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


Die Diskussion zu dieser Frage wurde in diesen Chatraum verschoben .
Paul White sagt GoFundMonica

Antworten:


18

Wie von erklärt worden Paul White in seinem Blog - Post: Inside the Optimizer: Row Tore In Tiefe die EXISTSeine Reihe Ziel einleitet, die bevorzugt NESTED LOOPSoder MERGE JOINüberHASH MATCH

Als letztes Beispiel sei angenommen, dass ein logischer Semi-Join (wie eine mit EXISTS eingeführte Unterabfrage) das Gesamtthema teilt: Es sollte optimiert werden, um die erste übereinstimmende Zeile schnell zu finden.

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 EXISTSvon Ihrer Abfrage zu verwenden.

Möglicherweise müssen Sie Ihre Abfrage mit a neu schreiben LEFT OUTER JOINund überprüfen, ob in smalltable keine Zeile vorhanden ist, indem Sie auf testenNULL

If EXISTS(
    SELECT databasename
    FROM somedb.dbo.bigtable l
    LEFT JOIN dbo.smalltable c ON c.source = l.source
    WHERE databasename = 'someval'
    AND source <> 'kt'
    AND c.source IS NULL
)

Sie könnten wahrscheinlich auch eine EXCEPTAbfrage verwenden, abhängig von der Anzahl der zu vergleichenden Felder:

If EXISTS(
   SELECT source
   FROM somedb.dbo.bigtable l
   WHERE databasename = 'someval'
   AND source <> 'kt'

   EXCEPT

   SELECT source
   FROM dbo.smalltable
)

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


0

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).

If not exists(
    select databasename 
    from somedb.dbo.bigtable l
    inner hash join dbo.smalltable c 
        on c.source = l.source
where databasename ='someval' and source  <>'kt')
begin
    Raiserror('Source missing',16,1)
    Return
end

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.



0

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:

IF
(
    SELECT
        COUNT(l.databasename) + 1 AS databasename
    FROM somedb.dbo.bigtable AS l

    WHERE   l.databasename ='someval'
        AND l.[source]  <> 'kt'
        AND NOT EXISTS(SELECT 1 FROM dbo.smalltable AS c WHERE c.[source]=l.[source])
) > 1 --Acts like EXISTS
BEGIN
    RAISERROR('Source missing', 16, 1)
RETURN
END

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!

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.