Los ejemplos en la pregunta no producen los mismos resultados (el OFFSET
ejemplo tiene un error off-by-one). Los formularios actualizados a continuación solucionan ese problema, eliminan la clasificación adicional para el ROW_NUMBER
caso y usan variables para hacer que la solución sea más general:
DECLARE
@PageSize bigint = 10,
@PageNumber integer = 3;
WITH Numbered AS
(
SELECT TOP ((@PageNumber + 1) * @PageSize)
o.*,
rn = ROW_NUMBER() OVER (
ORDER BY o.[object_id])
FROM #objects AS o
ORDER BY
o.[object_id]
)
SELECT
x.name,
x.[object_id],
x.principal_id,
x.[schema_id],
x.parent_object_id,
x.[type],
x.type_desc,
x.create_date,
x.modify_date,
x.is_ms_shipped,
x.is_published,
x.is_schema_published
FROM Numbered AS x
WHERE
x.rn >= @PageNumber * @PageSize
AND x.rn < ((@PageNumber + 1) * @PageSize)
ORDER BY
x.[object_id];
SELECT
o.name,
o.[object_id],
o.principal_id,
o.[schema_id],
o.parent_object_id,
o.[type],
o.type_desc,
o.create_date,
o.modify_date,
o.is_ms_shipped,
o.is_published,
o.is_schema_published
FROM #objects AS o
ORDER BY
o.[object_id]
OFFSET @PageNumber * @PageSize - 1 ROWS
FETCH NEXT @PageSize ROWS ONLY;
El ROW_NUMBER
plan tiene un costo estimado de 0.0197935 :
El OFFSET
plan tiene un costo estimado de 0.0196955 :
Eso es un ahorro de 0.000098 unidades de costo estimado (aunque el OFFSET
plan requeriría operadores adicionales si desea devolver un número de fila para cada fila). El OFFSET
plan seguirá siendo un poco más barato, en general, pero recuerde que los costos estimados son exactamente eso: aún se requieren pruebas reales. La mayor parte del costo en ambos planes es el costo del tipo completo del conjunto de entrada, por lo que los índices útiles beneficiarían a ambas soluciones.
Cuando se utilizan valores literales constantes (p. Ej., OFFSET 30
En el ejemplo original), el optimizador puede usar una clasificación TopN en lugar de una clasificación completa seguida de una clasificación Top. Cuando las filas necesarias del TopN Sort son un literal constante y <= 100 (la suma de OFFSET
y FETCH
) el motor de ejecución puede usar un algoritmo de ordenación diferente que puede funcionar más rápido que el TopN generalizado. Los tres casos tienen características de rendimiento diferentes en general.
En cuanto a por qué el optimizador no transforma automáticamente el ROW_NUMBER
patrón de sintaxis para usar OFFSET
, hay una serie de razones:
- Es casi imposible escribir una transformación que coincida con todos los usos existentes
- Tener algunas consultas de paginación se transforman automáticamente y otras no pueden ser confusas.
- No
OFFSET
se garantiza que el plan sea mejor en todos los casos.
Un ejemplo para el tercer punto anterior ocurre cuando el conjunto de paginación es bastante amplio. Puede ser mucho más eficiente buscar las claves necesarias utilizando un índice no agrupado y buscar manualmente en el índice agrupado en comparación con escanear el índice con OFFSET
o ROW_NUMBER
. Hay problemas adicionales a considerar si la aplicación de paginación necesita saber cuántas filas o páginas hay en total. Hay otra buena discusión sobre los méritos relativos de la tecla 'buscar' y 'compensar' métodos aquí .
En general, probablemente sea mejor que las personas tomen una decisión informada de cambiar sus consultas de paginación para usar OFFSET
, si corresponde, después de una prueba exhaustiva.