Für das folgende Schema und Beispieldaten
CREATE TABLE T
(
A INT NULL,
B INT NOT NULL IDENTITY,
C CHAR(8000) NULL,
UNIQUE CLUSTERED (A, B)
)
INSERT INTO T
(A)
SELECT NULLIF(( ( ROW_NUMBER() OVER (ORDER BY @@SPID) - 1 ) / 1003 ), 0)
FROM master..spt_values
Eine Anwendung verarbeitet die Zeilen aus dieser Tabelle in Clustered-Index-Reihenfolge in 1.000 Zeilenblöcken.
Die ersten 1.000 Zeilen werden aus der folgenden Abfrage abgerufen.
SELECT TOP 1000 *
FROM T
ORDER BY A, B
Die letzte Reihe dieses Sets befindet sich unten
+------+------+
| A | B |
+------+------+
| NULL | 1000 |
+------+------+
Gibt es eine Möglichkeit, eine Abfrage zu schreiben, die nur nach diesem zusammengesetzten Indexschlüssel sucht und ihm dann folgt, um den nächsten Teil von 1000 Zeilen abzurufen?
/*Pseudo Syntax*/
SELECT TOP 1000 *
FROM T
WHERE (A, B) is_ordered_after (@A, @B)
ORDER BY A, B
Die niedrigste Anzahl von Lesevorgängen, die ich bisher erreicht habe, ist 1020, aber die Abfrage scheint viel zu kompliziert zu sein. Gibt es einen einfacheren Weg zu gleicher oder besserer Effizienz? Vielleicht schafft es einer, alles in einem Bereich zu suchen?
DECLARE @A INT = NULL, @B INT = 1000
;WITH UnProcessed
AS (SELECT *
FROM T
WHERE ( EXISTS(SELECT A
INTERSECT
SELECT @A)
AND B > @B )
UNION ALL
SELECT *
FROM T
WHERE @A IS NULL AND A IS NOT NULL
UNION ALL
SELECT *
FROM T
WHERE A > @A
)
SELECT TOP 1000 *
FROM UnProcessed
ORDER BY A,
B
FWIW: Wenn die Spalte A
erstellt NOT NULL
und -1
stattdessen ein Sentinel-Wert von verwendet wird, sieht der entsprechende Ausführungsplan mit Sicherheit einfacher aus
Aber der einzelne Suchoperator im Plan führt immer noch zwei Suchvorgänge durch, anstatt ihn in einen zusammenhängenden Bereich zu reduzieren, und die logischen Lesevorgänge sind weitgehend gleich. Ich vermute also, dass dies so gut wie möglich ist.
quelle
NULL
Werte immer an erster Stelle stehen. (Gegenteil angenommen.) Korrigierter Zustand bei Fiddle(NULL, 1000 )
@A
anscheinend kein Scan durchgeführt , unabhängig davon, ob Null ist oder nicht. Aber ich kann nicht verstehen, ob die Pläne besser sind als Ihre Anfrage. Fiddle-2Antworten:
Eine meiner Lieblingslösungen ist die Verwendung eines
API
Cursors:Die Gesamtstrategie ist ein einzelner Scan, bei dem die Position zwischen den Aufrufen gespeichert wird. Die Verwendung eines
API
Cursors bedeutet, dass wir einen Zeilenblock zurückgeben können, anstatt wie bei einemT-SQL
Cursor einen nach dem anderen:Die
STATISTICS IO
Ausgabe ist:quelle