La paginación (salida por páginas) permite procesar grandes resultados de SELECT sin sobrecargar innecesariamente el servidor y la aplicación. Principales enfoques:
Ejemplo (LIMIT/OFFSET)
SELECT * FROM Orders ORDER BY OrderID LIMIT 100 OFFSET 1000;
Esta consulta devuelve las filas 1011–1110. Pero el OFFSET obliga al servidor a volver a ordenar y omitir las primeras 1000 filas, por lo que con paginación profunda se vuelve lento.
Ejemplo (método Keyset/Seek)
SELECT * FROM Orders WHERE OrderID > 1110 ORDER BY OrderID LIMIT 100;
Esta consulta busca rápidamente la siguiente página, sin gastar recursos en contar OFFSET, especialmente eficaz con un índice sobre OrderID.
Trampa: "¿Por qué la paginación a través de LIMIT/OFFSET puede funcionar mal con grandes volúmenes de datos y cómo mejorar esto?"
Respuesta: La dificultad radica en que OFFSET requiere que el servidor escanee y ordene todas las filas anteriores, por lo que cuanto más profundo se vayan las páginas, más lento será. La optimización consiste en pasar a la paginación por clave/seeker: elegir no por desplazamiento, sino por la clave del último registro de la página anterior.
-- Obteniendo la siguiente página por clave SELECT * FROM Orders WHERE OrderID > @LastOrderID ORDER BY OrderID LIMIT 100;
Historia
Proyecto: Marketplace, base de pedidos por 5 años (50 millones de filas)
Error: Se utilizó OFFSET para la paginación de pedidos de usuarios antiguos. Las consultas con OFFSET > 1 millón empezaron a tardar de 30 a 60 segundos. Esto afectó a los informes y API — causando una carga en la CPU del servidor y aumentando el tiempo de espera en la cola.
Historia
Proyecto: CRM corporativo, informes sobre clientes.
Error: En la paginación no se consideró el orden de clasificación y no se utilizaron índices. La rendimiento y el control de integridad de los conjuntos disminuyeron — los usuarios recibieron las mismas filas en diferentes páginas al realizar cambios en la tabla.
Historia
Proyecto: Plataforma financiera, cuadros de mando.
Error: Se construyeron paginaciones complejas a través de SQL dinámico generado sin variables bind, lo que llevó a inyecciones SQL y problemas con el mantenimiento de la transaccionalidad entre páginas de datos.