Uma lista de pedidos usa LIMIT 20 OFFSET 20 para abrir a segunda página. Se um pedido entra no topo entre as duas consultas, o limite entre as páginas muda e um pedido pode aparecer duas vezes. Um cursor baseado no último item visto mantém a referência à posição anterior.
SELECT id, created_at
FROM orders
WHERE (created_at, id) < ($1, $2)
ORDER BY created_at DESC, id DESC
LIMIT 20;
O cursor carrega created_at e id do último item devolvido. Use os mesmos filtros em todas as páginas e garanta que as colunas do cursor não sejam nulas. O id desempata registros que têm a mesma data. A primeira página usa a mesma ordenação sem a condição do cursor.
OFFSET ainda é adequado quando você precisa pular para um número de página em uma lista pequena. Com cursor, voltar para um ponto arbitrário exige uma referência guardada. Se os valores usados na ordenação mudam durante a navegação, defina se o produto aceita essa variação ou precisa de uma visão consistente da lista.
Documentação de referência · Como ler EXPLAIN ANALYZE no PostgreSQL para investigar uma consulta lenta
A ordenação precisa ser total
Uma ordenação apenas por created_at não define a posição relativa de dois registros com o mesmo instante. Inclua uma coluna única, como id, como desempate e use a mesma direção na consulta e no cursor. O predicado de continuação precisa seguir essa tupla. Se a ordenação tem várias colunas, o cursor transporta todas as chaves necessárias para reconstruir a posição.
Para navegar em sentido inverso, inverta a comparação e a ordenação com cuidado, limite os resultados e depois reorganize-os para apresentação. Se o cursor é exposto ao cliente, trate-o como dado não confiável. Codificar a tupla em Base64 não a torna secreta nem autenticada. Assine ou valide o cursor se ele precisa permanecer vinculado aos filtros, ao usuário ou à versão da consulta.
Decida se páginas enxergam novas inserções
Uma paginação por cursor costuma ser eficiente para avançar, mas não congela toda a lista. Registros inseridos antes do cursor podem não aparecer na navegação atual, enquanto alterações nas chaves de ordenação podem deslocar itens. Para um relatório que deve representar um instante consistente, use um snapshot ou um marcador de corte compatível com o volume e a retenção dos dados.
Teste fronteiras com valores iguais, lista vazia, exclusão entre páginas e item inserido após a primeira consulta. Confirme que não há duplicatas ou lacunas segundo o contrato. O índice deve acompanhar filtros e ordenação usados juntos; meça a consulta em uma tabela de tamanho plausível antes de escolher a estratégia apenas pelo nome cursor ou offset.
Um cursor opaco pode carregar a tupla de ordenação e os filtros ativos, mas ainda precisa de validação de formato e tamanho. Não aceite um cursor de uma consulta diferente se ele altera escopo ou autorização. Ao mudar o filtro no cliente, descarte o cursor antigo e comece pela primeira página. No teste de integração, compare a união das páginas com o conjunto ordenado esperado e repita a consulta de fronteira para detectar duplicação.