Dla następującego schematu i przykładowych danych
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
Aplikacja przetwarza wiersze z tej tabeli w kolejności indeksów klastrowych w 1000 porcjach wiersza.
Pierwsze 1000 wierszy jest pobieranych z następującego zapytania.
SELECT TOP 1000 *
FROM T
ORDER BY A, B
Ostatni rząd tego zestawu znajduje się poniżej
+------+------+
| A | B |
+------+------+
| NULL | 1000 |
+------+------+
Czy jest jakiś sposób na napisanie zapytania, które szuka tylko tego złożonego klucza indeksu, a następnie podąża za nim, aby pobrać następną porcję 1000 wierszy?
/*Pseudo Syntax*/
SELECT TOP 1000 *
FROM T
WHERE (A, B) is_ordered_after (@A, @B)
ORDER BY A, B
Najniższa liczba odczytów, jaką udało mi się dotychczas uzyskać, to 1020, ale zapytanie wydaje się zdecydowanie zbyt skomplikowane. Czy istnieje prostszy sposób na zapewnienie równej lub lepszej wydajności? Być może taki, któremu uda się to wszystko zrobić w jednym zasięgu?
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: Jeśli kolumna A
zostanie utworzona, NOT NULL
a -1
zamiast niej zostanie użyta wartość wartownika, równoważny plan wykonania z pewnością wygląda na prostszy
Ale operator wyszukiwania pojedynczego w planie nadal wykonuje dwa poszukiwania, zamiast łączyć je w jeden ciągły zakres, a logiczne odczyty są w przybliżeniu takie same, więc podejrzewam, że być może jest to tak dobre, jak to możliwe?
(NULL, 1000 )
@A
ma wartość zerową, czy nie, wydaje się, że nie wykonuje skanowania. Ale nie rozumiem, czy plany są lepsze niż twoje zapytanie. Fiddle-2
NULL
wartości są zawsze pierwsze. (zakładał się odwrotnie.) Poprawiony stan w Fiddle