Para el siguiente esquema y datos de ejemplo
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
Una aplicación está procesando las filas de esta tabla en orden de índice agrupado en 1,000 fragmentos de fila.
Las primeras 1000 filas se recuperan de la siguiente consulta.
SELECT TOP 1000 *
FROM T
ORDER BY A, B
La fila final de ese conjunto está debajo
+------+------+
| A | B |
+------+------+
| NULL | 1000 |
+------+------+
¿Hay alguna forma de escribir una consulta que solo busque en esa clave de índice compuesta y luego la siga para recuperar el siguiente fragmento de 1000 filas?
/*Pseudo Syntax*/
SELECT TOP 1000 *
FROM T
WHERE (A, B) is_ordered_after (@A, @B)
ORDER BY A, B
El número más bajo de lecturas que he logrado hasta ahora es 1020, pero la consulta parece demasiado complicada. ¿Existe una forma más simple de igual o mejor eficiencia? Tal vez uno que logra hacer todo en un rango de búsqueda?
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: Si la columna A
está hecha NOT NULL
y un valor centinela de -1
su lugar se usa el plan de ejecución equivalente sin duda parece más sencillo
Pero el operador de búsqueda única en el plan todavía realiza dos búsquedas en lugar de colapsarlo en un solo rango contiguo y las lecturas lógicas son muy similares, así que sospecho que tal vez esto sea tan bueno como sea posible.
fuente
NULL
valores son siempre los primeros. (asumió lo contrario.) Condición corregida en Fiddle(NULL, 1000 )
@A
es nulo o no, parece que no hace un escaneo. Pero no puedo entender si los planes son mejores que su consulta. Fiddle-2Respuestas:
Una de mis soluciones favoritas es usar un
API
cursor:La estrategia general es un escaneo único que recuerda su posición entre llamadas. Usar un
API
cursor significa que podemos devolver un bloque de filas en lugar de uno a la vez, como sería el caso con unT-SQL
cursor:El
STATISTICS IO
resultado es:fuente