Ich habe eine Klasse von Abfragen, die die Existenz eines von zwei Dingen testen. Es ist von der Form
SELECT CASE
WHEN EXISTS (SELECT 1 FROM ...)
OR EXISTS (SELECT 1 FROM ...)
THEN 1 ELSE 0 END;
Die eigentliche Anweisung wird in C generiert und als Ad-hoc-Abfrage über eine ODBC-Verbindung ausgeführt.
Kürzlich hat sich herausgestellt, dass das zweite SELECT in den meisten Fällen wahrscheinlich schneller sein wird als das erste SELECT, und dass das Ändern der Reihenfolge der beiden EXISTS-Klauseln in mindestens einem missbräuchlichen Testfall, den wir gerade erstellt hatten, zu einer drastischen Beschleunigung führte.
Das Offensichtliche ist, einfach die beiden Klauseln zu wechseln, aber ich wollte sehen, ob jemand, der mit SQL Server besser vertraut ist, dies abwägen möchte. Es fühlt sich so an, als würde ich mich auf Zufall und ein "Implementierungsdetail" verlassen.
(Es scheint auch so, als würde SQL Server, wenn es intelligenter wäre, beide EXISTS-Klauseln parallel ausführen und den jeweils ersten zuerst den anderen kurzschließen lassen.)
Gibt es eine bessere Möglichkeit, SQL Server dazu zu bringen, die Laufzeit einer solchen Abfrage konsistent zu verbessern?
Aktualisieren
Vielen Dank für Ihre Zeit und Ihr Interesse an meiner Frage. Ich hatte keine Fragen zu den tatsächlichen Abfrageplänen erwartet, bin aber bereit, sie zu teilen.
Dies gilt für eine Softwarekomponente, die SQL Server 2008R2 und höher unterstützt. Die Form der Daten kann je nach Konfiguration und Verwendung sehr unterschiedlich sein. Mein Kollege hat darüber nachgedacht, diese Änderung an der Abfrage vorzunehmen, da die (im Beispiel) dbf_1162761$z$rv$1257927703Tabelle immer mehr oder gleich der Anzahl der Zeilen enthält als die dbf_1162761$z$dd$1257927703Tabelle - manchmal deutlich mehr (Größenordnungen).
Hier ist der missbräuchliche Fall, den ich erwähnt habe. Die erste Abfrage ist die langsame und dauert etwa 20 Sekunden. Die zweite Abfrage wird sofort abgeschlossen.
Für das, was es wert ist, wurde kürzlich auch das Bit "OPTIMIZE FOR UNKNOWN" hinzugefügt, da das Parameter-Sniffing bestimmte Fälle verwarf.
Ursprüngliche Abfrage:
SELECT CASE
WHEN EXISTS (SELECT 1 FROM zumero.dbf_1162761$z$rv$1257927703 rv INNER JOIN zumero.dbf_1162761$t$tx tx ON tx.txid=rv.txid WHERE tx.generation BETWEEN 1500 AND 2502)
OR EXISTS (SELECT 1 FROM zumero.dbf_1162761$z$dd$1257927703 dd INNER JOIN zumero.dbf_1162761$t$tx tx ON tx.txid=dd.txid WHERE tx.generation BETWEEN 1500 AND 2502)
THEN 1 ELSE 0 END
OPTION (OPTIMIZE FOR UNKNOWN)
Ursprünglicher Plan:
|--Compute Scalar(DEFINE:([Expr1006]=CASE WHEN [Expr1007] THEN (1) ELSE (0) END))
|--Nested Loops(Left Semi Join, DEFINE:([Expr1007] = [PROBE VALUE]))
|--Constant Scan
|--Concatenation
|--Nested Loops(Inner Join, WHERE:([scale].[zumero].[dbf_1162761$z$rv$1257927703].[txid] as [rv].[txid]=[scale].[zumero].[dbf_1162761$t$tx].[txid] as [tx].[txid]))
| |--Clustered Index Scan(OBJECT:([scale].[zumero].[dbf_1162761$z$rv$1257927703].[PK__dbf_1162__97770A2F62EEAE79] AS [rv]), WHERE:([scale].[zumero].[dbf_1162761$z$rv$1257927703].[txid] as [rv].[txid]>(0)))
| |--Index Seek(OBJECT:([scale].[zumero].[dbf_1162761$t$tx].[gendex] AS [tx]), SEEK:([tx].[generation] >= (1500) AND [tx].[generation] <= (2502)) ORDERED FORWARD)
|--Nested Loops(Inner Join, OUTER REFERENCES:([tx].[txid]))
|--Clustered Index Scan(OBJECT:([scale].[zumero].[dbf_1162761$t$tx].[PK__dbf_1162__E3BA953EC2197789] AS [tx]), WHERE:([scale].[zumero].[dbf_1162761$t$tx].[generation] as [tx].[generation]>=(1500) AND [scale].[zumero].[dbf_1162761$t$tx].[generation] as [tx].[generation]<=(2502)) ORDERED FORWARD)
|--Index Seek(OBJECT:([scale].[zumero].[dbf_1162761$z$dd$1257927703].[n$dbf_1162761$z$dd$txid$1257927703] AS [dd]), SEEK:([dd].[txid]=[scale].[zumero].[dbf_1162761$t$tx].[txid] as [tx].[txid]), WHERE:([scale].[zumero].[dbf_1162761$z$dd$1257927703].[txid] as [dd].[txid]>(0)) ORDERED FORWARD)
Feste Abfrage:
SELECT CASE
WHEN EXISTS (SELECT 1 FROM zumero.dbf_1162761$z$dd$1257927703 dd INNER JOIN zumero.dbf_1162761$t$tx tx ON tx.txid=dd.txid WHERE tx.generation BETWEEN 1500 AND 2502)
OR EXISTS (SELECT 1 FROM zumero.dbf_1162761$z$rv$1257927703 rv INNER JOIN zumero.dbf_1162761$t$tx tx ON tx.txid=rv.txid WHERE tx.generation BETWEEN 1500 AND 2502)
THEN 1 ELSE 0 END
OPTION (OPTIMIZE FOR UNKNOWN)
Fester Plan:
|--Compute Scalar(DEFINE:([Expr1006]=CASE WHEN [Expr1007] THEN (1) ELSE (0) END))
|--Nested Loops(Left Semi Join, DEFINE:([Expr1007] = [PROBE VALUE]))
|--Constant Scan
|--Concatenation
|--Nested Loops(Inner Join, OUTER REFERENCES:([tx].[txid]))
| |--Clustered Index Scan(OBJECT:([scale].[zumero].[dbf_1162761$t$tx].[PK__dbf_1162__E3BA953EC2197789] AS [tx]), WHERE:([scale].[zumero].[dbf_1162761$t$tx].[generation] as [tx].[generation]>=(1500) AND [scale].[zumero].[dbf_1162761$t$tx].[generation] as [tx].[generation]<=(2502)) ORDERED FORWARD)
| |--Index Seek(OBJECT:([scale].[zumero].[dbf_1162761$z$dd$1257927703].[n$dbf_1162761$z$dd$txid$1257927703] AS [dd]), SEEK:([dd].[txid]=[scale].[zumero].[dbf_1162761$t$tx].[txid] as [tx].[txid]), WHERE:([scale].[zumero].[dbf_1162761$z$dd$1257927703].[txid] as [dd].[txid]>(0)) ORDERED FORWARD)
|--Nested Loops(Inner Join, WHERE:([scale].[zumero].[dbf_1162761$z$rv$1257927703].[txid] as [rv].[txid]=[scale].[zumero].[dbf_1162761$t$tx].[txid] as [tx].[txid]))
|--Clustered Index Scan(OBJECT:([scale].[zumero].[dbf_1162761$z$rv$1257927703].[PK__dbf_1162__97770A2F62EEAE79] AS [rv]), WHERE:([scale].[zumero].[dbf_1162761$z$rv$1257927703].[txid] as [rv].[txid]>(0)))
|--Index Seek(OBJECT:([scale].[zumero].[dbf_1162761$t$tx].[gendex] AS [tx]), SEEK:([tx].[generation] >= (1500) AND [tx].[generation] <= (2502)) ORDERED FORWARD)

