SQL · orden y paginación

Paginación keyset en SQL: continuar desde la última fila

Pagina resultados usando los valores de la última fila vista como cursor, en lugar de contar y descartar todas las filas anteriores con un OFFSET cada vez mayor.

Lectura: 8–10 minEjemplo comprobadoPráctica con pistas
Contenido de esta guía

Respuesta rápida

La paginación keyset —también llamada por cursor— pide «las siguientes filas después de esta clave». Necesita un ORDER BY estable y reutiliza los valores de la última fila de la página anterior dentro de WHERE.

Cursor compuesto descendente
SELECT id, fecha, total
FROM pedidos
WHERE fecha < :ultima_fecha
   OR (fecha = :ultima_fecha AND id < :ultimo_id)
ORDER BY fecha DESC, id DESC
LIMIT 5;

El cursor contiene dos datos de la última fila: ultima_fecha y ultimo_id. La condición debe avanzar en la misma dirección lógica que el orden.

Modelo mental: deja de pensar en «página 37»

Con OFFSET una aplicación suele pedir «omite 180 filas y dame las 20 siguientes». Con keyset pide «continúa después del último pedido que ya mostré». Esa diferencia evita convertir la posición absoluta de una fila en el punto de partida de cada consulta.

El patrón encaja especialmente bien con feeds, historiales, listados cronológicos y botones «cargar más». No es igual de cómodo si necesitas saltar directamente a una página numerada arbitraria.

El cursor no es una fila oculta. Es el conjunto mínimo de valores necesarios para continuar el mismo orden sin ambigüedad. Si ordenas por fecha DESC, id DESC, conserva ambos.

Ejemplo con la base educativa

La primera consulta no necesita cursor: solo fija un orden total y limita el bloque.

Primera página
SELECT id, fecha, total
FROM pedidos
ORDER BY fecha DESC, id DESC
LIMIT 5;
Primera página comprobada en SQLite
idfechatotal
1182025-06-16179,00
1172025-06-14148,90
1162025-06-12999,00
1152025-06-1024,00
1142025-06-08203,90

El cursor para continuar es fecha = '2025-06-08' e id = 114.

Siguiente página
SELECT id, fecha, total
FROM pedidos
WHERE fecha < '2025-06-08'
   OR (fecha = '2025-06-08' AND id < 114)
ORDER BY fecha DESC, id DESC
LIMIT 5;
Siguiente página comprobada
idfechatotal
1132025-06-0698,00
1122025-06-0354,00
1112025-06-01249,00
1102025-05-2861,50
1092025-05-2589,00

Por qué necesitas un desempate único

Si ordenas solo por fecha y dos pedidos comparten la misma fecha, el cursor no sabe qué filas de ese empate ya aparecieron. Añadir id —si es único— crea un orden total y permite continuar dentro de la misma fecha.

Cambiar el ORDER BY entre páginas

El filtro del cursor y el ORDER BY describen el mismo recorrido. Si cambias columnas, dirección o colación sin regenerar el cursor, puedes repetir o saltar filas.

Qué mejora frente a OFFSET y qué no

PostgreSQL documenta que las filas descartadas por un OFFSET grande siguen teniendo que calcularse dentro del servidor. Keyset evita basarse en una cantidad creciente de filas que deben saltarse y permite que un índice alineado con el filtro y el orden sea una buena candidata para continuar el recorrido.

También reduce el desplazamiento posicional causado por inserciones anteriores al cursor. Sin embargo, no crea por sí sola una fotografía inmutable de los datos: cambios posteriores todavía pueden alterar qué filas existen o qué valores tienen. Si necesitas consistencia de snapshot entre muchas peticiones, eso pertenece al diseño transaccional y de la aplicación.

Índice candidato y comprobación

Hipótesis de índice
CREATE INDEX idx_pedidos_fecha_id
ON pedidos (fecha DESC, id DESC);

Es una hipótesis razonable para este patrón, no una promesa de rendimiento. Comprueba el plan con la herramienta de tu motor y mide con datos representativos. El tratamiento de direcciones de índice y el plan concreto varían entre motores.

Compatibilidad entre motores

Keyset no es una cláusula SQL especial: combina comparaciones, WHERE, ORDER BY y una forma de limitar filas. PostgreSQL, MySQL y SQLite permiten LIMIT; SQL Server puede limitar con TOP o con OFFSET/FETCH según el diseño. La forma de los parámetros (:nombre, ?, @nombre) pertenece al driver o framework.

Algunos motores admiten comparar valores de fila, por ejemplo (fecha, id) < (?, ?). La forma explícita con OR de esta guía muestra la lógica sin depender de ese atajo y facilita adaptarla a dialectos distintos.

Práctica: continúa después del pedido 111

Intermedia · orden y filtros

Devuelve las tres filas siguientes

El último pedido visible tiene fecha = '2025-06-01' e id = 111. Continúa el orden fecha DESC, id DESC y devuelve tres filas.

Qué debes recordar

  • Keyset continúa desde los valores de la última fila vista.
  • El orden debe ser total y estable; añade una clave única para desempatar.
  • La condición del cursor debe avanzar en la misma dirección que ORDER BY.
  • Evita offsets crecientes, pero no sustituye una estrategia de consistencia transaccional.

Conceptos relacionados

Fuentes técnicas consultadas

Las referencias oficiales se usaron para verificar el coste conceptual de OFFSET, el orden y las variantes de límite.