Для наведеної нижче схеми та прикладу даних
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
Додаток обробляє рядки з цієї таблиці у кластеризованому порядку індексом у 1000 фрагментах рядків.
Перші 1000 рядків отримують із наступного запиту.
SELECT TOP 1000 *
FROM T
ORDER BY A, B
Останній ряд цього набору знаходиться нижче
+------+------+
| A | B |
+------+------+
| NULL | 1000 |
+------+------+
Чи є спосіб написати запит, який просто шукає цей складний індексний ключ, а потім слідує за ним, щоб отримати наступний фрагмент з 1000 рядків?
/*Pseudo Syntax*/
SELECT TOP 1000 *
FROM T
WHERE (A, B) is_ordered_after (@A, @B)
ORDER BY A, B
Найменша кількість читань, які мені вдалося отримати до цього часу, - 1020, але запит здається занадто суперечливим. Чи є простіший спосіб однакової чи кращої ефективності? Можливо, той, кому вдається зробити це все в одному діапазоні?
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: Якщо стовпець A
створений NOT NULL
і -1
замість нього використовується дозорне значення , замість цього еквівалентний план виконання звичайно виглядає простішим
Але оператор одноосібного пошуку в плані все ж виконує два пошуки, а не згортання його в єдиний суміжний діапазон, і логічні зчитування майже однакові, тому я підозрюю, що, можливо, це є настільки ж добре, як це вийде?
(NULL, 1000 )
@A
, нульова чи ні, схоже, вона не робить сканування. Але я не можу зрозуміти, чи плани краще, ніж ваш запит. Fiddle-2
NULL
значення завжди на першому місці. (Передбачається зворотне.) Виправлена умова в Fiddle