Paginación en SQL Server


17

Tengo una base de datos muy grande, aproximadamente 100 GB. Estoy ejecutando consulta:

select * from <table_name>;

y quiero mostrar solo las filas 100 a 200.

Quiero entender cómo sucede esto internamente. ¿La base de datos recupera todos los registros del disco en la memoria y envía de vuelta las filas 100 a 400 al cliente que realiza la consulta? ¿O existe algún mecanismo, de modo que solo esos registros (100º-200º) se obtengan de la base de datos, mediante el uso de mecanismos de indexación como árboles B, etc.?

Descubrí que esto está relacionado con el concepto de paginación, pero no pude encontrar exactamente cómo sucede internamente en el nivel de la base de datos.

Respuestas:


37

En la consulta que publicaste:

select * from <table_name>;

No existen las filas 100a-200a, porque no especificas ORDER BY. El pedido no está garantizado a menos que incluya ORDER BY por muchas razones interesantes, pero ese no es realmente el punto aquí.

Entonces, para ilustrar su punto, usemos una tabla: voy a usar la tabla Usuarios del volcado de datos de Desbordamiento de pila y ejecutaré esta consulta:

SELECT * FROM dbo.Users ORDER BY DisplayName;

De manera predeterminada, no hay índice en el campo DisplayName, por lo que SQL Server tiene que escanear toda la tabla y luego ordenarla por DisplayName. Aquí está el plan de ejecución :

Escaneo de índice agrupado con una clasificación

No es bonito, es mucho trabajo, con un costo estimado de subárbol de alrededor de 30k. (Puede verlo pasando el mouse sobre el operador de selección en PasteThePlan). Entonces, ¿qué sucede si solo queremos las filas 100-200? Podemos usar esta sintaxis en SQL Server 2012+:

SELECT * FROM dbo.Users ORDER BY DisplayName OFFSET 100 ROWS FETCH NEXT 100 ROWS ONLY;

El plan de ejecución también es bastante feo:

Escaneo de índice agrupado con una clasificación y una parte superior

SQL Server todavía está escaneando toda la tabla para crear la lista ordenada solo para darle sus filas 100-200, y el costo sigue siendo de alrededor de 30k. Peor aún, esta lista completa se reconstruirá cada vez que se ejecute su consulta (porque, después de todo, alguien podría haber cambiado su DisplayName).

Para que sea más rápido, podemos crear un índice no agrupado en DisplayName, que es una copia de nuestra tabla, ordenada por ese campo específico:

CREATE INDEX IX_DisplayName ON dbo.Users(DisplayName);

Con ese índice, el plan de ejecución de nuestra consulta ahora realiza una búsqueda de índice:

Búsqueda de índice y búsqueda de clave

La consulta finaliza instantáneamente y tiene un costo estimado de subárbol de solo 0.66 (en lugar de 30k).

En resumen, si organiza los datos de una manera que admita las consultas que ejecuta con frecuencia, entonces sí, SQL Server puede tomar atajos para acelerar sus consultas. Si, por otro lado, todo lo que tienes son montones o índices agrupados, estás jodido.


"De manera predeterminada, no hay índice en el campo DisplayName, por lo que SQL Server tiene que escanear toda la tabla y luego ordenarla por DisplayName". Disculpe si esta es una pregunta muy básica, en el caso que cité su respuesta, cuando dijo "Escanear toda la tabla", ¿eso significa que todos los datos se llevarán a la memoria y se ordenarán (que no se ve de la manera correcta)?
AV94

Por su respuesta, entiendo que si el campo está indexado, hacer consultas como: obtener la fila 100 a 200 es muy eficiente ya que SQL busca el índice (árbol B, etc.) y va directamente a ese punto (fila 100). ¿Podría decirme si esto es correcto?
AV94

@AnilVedala sobre su primera pregunta: sí, los datos deben ordenarse. ¿De qué otra forma podría una base de datos lograr eso con una lista sin clasificar?
Brent Ozar el

1
@AnilVedala sobre su segunda pregunta: ahí es donde entra el último plan de ejecución que le di. (Si está preguntando cómo leer un plan de ejecución, recoja el libro Planes de ejecución de Grant Fritchey).
Brent Ozar

15

Solo como una adición a la respuesta de Brent cuando se usa un índice que no cubre para evitar un tipo, existe un problema potencial con los números de página posteriores que se pueden ver al ejecutar el siguiente

SELECT * 
FROM dbo.Users 
ORDER BY DisplayName 
OFFSET 100000 ROWS 
FETCH NEXT 100 ROWS ONLY;

El plan de ejecución muestra que la búsqueda se ejecutó 100.100 veces a pesar de que el operador TOP filtra todas las filas excepto 100.

ingrese la descripción de la imagen aquí

Esto puede mitigarse utilizando el siguiente patrón

WITH T
     AS (SELECT Id,
                DisplayName
         FROM   dbo.Users
         ORDER  BY DisplayName
        OFFSET 100000 ROWS 
        FETCH NEXT 100 ROWS ONLY
        )
SELECT U.*
FROM   dbo.Users U
       JOIN T
         ON U.Id = T.Id
ORDER  BY T.DisplayName 

Esto filtra todo excepto las últimas 100 filas antes de realizar las búsquedas, lo que puede tener un impacto significativo en la velocidad para valores de desplazamiento grandes.

ingrese la descripción de la imagen aquí


3

Realmente depende de cómo implemente la paginación dentro de su consulta, la naturaleza de los datos y la forma en que está configurado su sistema. Es bastante seguro decir que SQL Server intentará devolver sus datos utilizando lo que considera que es la menor cantidad de esfuerzo posible. Si no tiene un orden de clasificación explícito, filtrado, agrupación o cualquier ventana, entonces SQL Server posiblemente podría optimizar el plan de consulta de modo que pudiera devolver solo las páginas del disco que contenían los datos requeridos por su consulta, o incluso mejor, directamente desde agrupación de almacenamiento intermedio. Tan pronto como comience a cambiar la consulta para incluir ordenación, agrupación, ventanas y filtrado, comenzará a complicarse.

Hay un muy buen artículo sobre el rendimiento de SQL aquí que entra en algunos detalles de los diversos métodos de paginación y cómo afectan el plan de consulta. Recomiendo leerlo y luego probar algunos de los diversos métodos que señalan y ver qué plan de consulta se elige en su propio sistema.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.