Para o seguinte esquema e dados de exemplo
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
Um aplicativo está processando as linhas desta tabela na ordem do índice clusterizado em blocos de 1.000 linhas.
As primeiras 1.000 linhas são recuperadas da consulta a seguir.
SELECT TOP 1000 *
FROM T
ORDER BY A, B
A linha final desse conjunto está abaixo
+------+------+
| A | B |
+------+------+
| NULL | 1000 |
+------+------+
Existe alguma maneira de escrever uma consulta que apenas busque essa chave de índice composto e a siga para recuperar o próximo bloco de 1.000 linhas?
/*Pseudo Syntax*/
SELECT TOP 1000 *
FROM T
WHERE (A, B) is_ordered_after (@A, @B)
ORDER BY A, B
O menor número de leituras que consegui até agora é 1020, mas a consulta parece muito complicada. Existe uma maneira mais simples de eficiência igual ou melhor? Talvez aquele que consegue fazer tudo em uma busca de alcance?
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: Se a coluna A
for feita NOT NULL
e um valor sentinela -1
for usado, o plano de execução equivalente certamente parecerá mais simples
Mas o único operador de busca no plano ainda realiza duas buscas em vez de reduzi-lo em um único intervalo contíguo e as leituras lógicas são praticamente as mesmas, então estou suspeitando que talvez isso seja tão bom quanto será.
Uma solução favorita minha é usar um
API
cursor:A estratégia geral é uma varredura única que lembra sua posição entre as chamadas. Usar um
API
cursor significa que podemos retornar um bloco de linhas em vez de uma por vez, como seria o caso de umT-SQL
cursor:A
STATISTICS IO
saída é: