Wenn ich den folgenden Code ausführe, dauert es 22,5 Minuten und liest 106 Millionen. Wenn ich jedoch nur die innere select-Anweisung selbst ausführe, dauert es nur 15 Sekunden und 264k liest. Als Randnotiz gibt die Auswahlabfrage keine Datensätze zurück.
Irgendeine Idee, warum das IF EXISTS
so viel länger laufen und so viel mehr liest? Ich habe auch die Select-Anweisung geändert SELECT TOP 1 [dlc].[id]
und sie nach 2 Minuten getötet.
Als vorübergehende Korrektur habe ich es geändert, um eine Zählung (*) durchzuführen und diesen Wert einer Variablen zuzuweisen @cnt
. Dann macht es eine IF 0 <> @cnt
Aussage. Aber ich dachte, es EXISTS
wäre besser, denn wenn Datensätze in der select-Anweisung zurückgegeben würden, würde die Suche abgebrochen, sobald mindestens ein Datensatz gefunden wurde, während count(*)
die vollständige Abfrage abgeschlossen wird. Was vermisse ich?
IF EXISTS
(SELECT [dlc].[ID]
FROM TableDLC [dlc]
JOIN TableD [d]
ON [d].[ID] = [dlc].[ID]
JOIN TableC [c]
ON [c].[ID] = [d].[ID2]
WHERE [c].[Name] <> [dlc].[Name])
BEGIN
<do something>
END
quelle
IF NOT EXISTS (...) BEGIN END ELSE BEGIN <do something> END
.Antworten:
Wie ich in meiner Antwort auf diese verwandte Frage erklärte:
Wie (und warum) wirkt sich TOP auf einen Ausführungsplan aus?
Using
EXISTS
führt ein Zeilenziel ein, bei dem das Optimierungsprogramm einen Ausführungsplan erstellt, mit dem die erste Zeile schnell lokalisiert werden soll. Dabei wird davon ausgegangen, dass die Daten gleichmäßig verteilt sind. Wenn Statistiken beispielsweise zeigen, dass 100 erwartete Übereinstimmungen in 100.000 Zeilen vorliegen, wird davon ausgegangen, dass nur 1.000 Zeilen gelesen werden müssen, um die erste Übereinstimmung zu finden.Dies führt zu längeren Ausführungszeiten als erwartet, wenn sich diese Annahme als fehlerhaft herausstellt. Wenn SQL Server beispielsweise eine Zugriffsmethode (z. B. einen ungeordneten Scan) auswählt, bei der der erste übereinstimmende Wert sehr spät in der Suche gefunden wird, kann dies zu einem fast vollständigen Scan führen. Wenn andererseits eine übereinstimmende Zeile in den ersten Zeilen gefunden wird, ist die Leistung sehr gut. Dies ist das grundlegende Risiko bei Zeilenzielen - inkonsistente Leistung.
Es ist normalerweise möglich, die Abfrage so umzuformulieren, dass kein Zeilenziel zugewiesen wird. Ohne das Zeilenziel kann die Abfrage immer noch beendet werden, wenn die erste übereinstimmende Zeile gefunden wird (wenn sie korrekt geschrieben wurde), aber die Strategie des Ausführungsplans ist wahrscheinlich anders (und hoffentlich effektiver). Offensichtlich erfordert count (*) das Lesen aller Zeilen, daher ist es keine perfekte Alternative.
Wenn Sie SQL Server 2008 R2 oder höher ausführen , können Sie im Allgemeinen auch das dokumentierte und unterstützte Ablaufverfolgungsflag 4138 verwenden , um einen Ausführungsplan ohne Zeilenziel abzurufen. Dieses Flag kann auch mithilfe des unterstützten Hinweises angegeben
OPTION (QUERYTRACEON 4138)
werden. Beachten Sie jedoch , dass dafür die Berechtigung sysadmin zur Laufzeit erforderlich ist , sofern dies nicht zusammen mit einem Planungshandbuch verwendet wird.Unglücklicherweise
Keines der oben genannten ist mit einer
IF EXISTS
bedingten Anweisung funktionsfähig . Dies gilt nur für reguläre DML. Es wird mit der alternativen arbeitenSELECT TOP (1)
Sie versucht Formulierung. Das kann durchaus besser sein als die VerwendungCOUNT(*)
, bei der, wie bereits erwähnt, alle qualifizierten Zeilen gezählt werden müssen.Es gibt jedoch eine Reihe von Möglichkeiten, diese Anforderung auszudrücken, mit denen Sie das Zeilenziel vermeiden oder steuern können, während Sie die Suche vorzeitig beenden. Ein letztes Beispiel:
quelle
Da EXISTS nur eine einzelne Zeile finden muss, wird ein Zeilenziel von eins verwendet. Dies kann manchmal zu einem weniger als idealen Plan führen. Wenn Sie erwarten, dass dies für Sie so ist, füllen Sie eine Variable mit dem Ergebnis von a
COUNT(*)
und testen Sie diese Variable, um festzustellen, ob sie größer als 0 ist.Also ... Mit einem kleinen Zeilenziel wird vermieden, dass Vorgänge blockiert werden, z. B. das Erstellen von Hashtabellen oder das Sortieren von Flüssen, die für Zusammenführungsverknüpfungen nützlich sein könnten, da sich herausstellt, dass etwas schnell gefunden werden muss und daher geschachtelte Schleifen erforderlich sind sei am besten wenn es etwas findet. Abgesehen davon, dass dies einen Plan erschweren kann, der über den gesamten Satz viel schlimmer ist. Wenn es schnell gehen würde, eine einzelne Zeile zu finden, würden Sie diese Methode gerne verwenden, um Blockierungen zu vermeiden ...
quelle