Quero implementar a paginação baseada em cursor para um grande conjunto de dados.
Com OFFSET
paginação baseada, quando o usuário quiser page N
, você apenas OFFSET
N * page_size
. A consulta termina assim:
SELECT *
FROM books
ORDER BY id
OFFSET 100000
LIMIT 10;
Mas OFFSET
fica mais lento quanto maior o valor dado porque o PostgreSQL precisa carregar e descartar as linhas anteriores.
Uma abordagem baseada em cursor é onde dizemos ao usuário "aqui está a página N
, e como o último registro nela tem , id
para X
obter a próxima página, você deve me pedir registros com id > X
." A consulta termina assim:
SELECT *
FROM books
WHERE id > 100000
ORDER BY id
LIMIT 10;
Nesse caso, o PostgreSQL pode carregar apenas as linhas necessárias.
Isso funciona muito bem ao classificar por arquivos id
. Mas eu gostaria de poder classificar por outras colunas - por exemplo, por title
- e ainda paginar.
O problema é que title
não é único. Portanto, se o registro final na página N
tiver o título "Sobre Doninhas" e houver vários livros com esse título, solicitar a próxima página WHERE title > 'About Weasels'
pode pular alguns deles.
Eu posso obter valores exclusivos tendo a solicitação do usuário WHERE (title, id) > ('About Weasels', 100000)
, mas isso funciona mal porque o PostgreSQL precisa calcular (title, id)
para cada linha.
Eu tentei adicionar um índice para pré-computar essa 2-tupla:CREATE INDEX books_title_and_id ON books (title, id);
...mas esse índice não faz diferença para o plano de consulta.
Existe um índice que eu poderia criar para acelerar essa consulta?
Aqui está um exemplo usando seu índice:
Observe que a primeira ocorrência de
title, id
(na comparação >) deve estar entre parênteses, e a segunda ocorrência (no ORDER BY) não deve estar entre parênteses.