Paginação: porque é que a página 500 demora tanto
A paginação por OFFSET é a primeira que se aprende e a que está em quase todo o lado. Funciona bem — e degrada-se de uma forma que só se nota quando a tabela cresce.
1. O problema
SELECT * FROM artigos ORDER BY publicado_em DESC LIMIT 20 OFFSET 10000;A base não salta 10 000 linhas: lê-as e deita-as fora, uma a uma, para chegar às vinte que interessam. A página 1 é instantânea, a página 500 lê meio milhão de linhas para mostrar vinte.
E há um segundo problema, mais insidioso: se alguém publicar um artigo enquanto o leitor passa da página 2 para a 3, tudo desliza uma posição — e um artigo que estava na 2 aparece outra vez na 3.
2. Paginação por cursor
Em vez de "salta 10 000", diz-se "dá-me o que vem depois daquele":
SELECT * FROM artigos
WHERE (publicado_em, id) < ('2026-03-27', 842)
ORDER BY publicado_em DESC, id DESC
LIMIT 20;Com um índice em (publicado_em DESC, id DESC), a base salta diretamente para o ponto e lê vinte linhas. A página 500 custa o mesmo que a primeira.
O id ao lado da data não é decoração: dois artigos publicados no mesmo dia precisam de um critério de desempate, ou a fronteira entre páginas fica indefinida e há linhas que se repetem ou desaparecem.
3. Na API
// Em vez de ?pagina=500
GET /api/artigos?depoisDe=2026-03-27_842&limite=20
{
"artigos": [ ... ],
"proximoCursor": "2026-03-10_811"
}O cursor é opaco para quem consome: é uma marca no sítio onde ficou, e não um número que se possa manipular.
4. Quando cada um serve
OFFSET — quando é preciso mostrar "página 7 de 43" e saltar para uma página qualquer. Numa administração com centenas de registos, é perfeito e mais simples.
Cursor — quando a lista é grande, cresce por cima, ou é percorrida de seguida: uma cronologia, um scroll infinito, uma API pública.
E há uma terceira via, que é a mais barata de todas: quando ninguém precisa da página 500, não a ofereça. Um limite de dez páginas mais uma pesquisa a sério resolve melhor o problema real.