Un grande OFFSET
sarà sempre lento. Postgres deve ordinare tutte le righe e contare i visibili quelli fino al tuo offset. Per saltare tutte le righe precedenti direttamente potresti aggiungere un row_number
indicizzato alla tabella (o creare una MATERIALIZED VIEW
incluso detto row_number
) e lavora con WHERE row_number > x
invece di OFFSET x
.
Tuttavia, questo approccio è valido solo per i dati di sola lettura (o principalmente). Implementazione dello stesso per i dati delle tabelle che possono cambiare contemporaneamente è più impegnativo. Devi iniziare definendo il comportamento desiderato esattamente .
Suggerisco un approccio diverso per la impaginazione :
SELECT *
FROM big_table
WHERE (vote, id) > (vote_x, id_x) -- ROW values
ORDER BY vote, id -- needs to be deterministic
LIMIT n;
Dove vote_x
e id_x
sono degli ultimi riga della pagina precedente (per entrambi DESC
e ASC
). O dal primo se si naviga indietro .
Il confronto dei valori di riga è supportato dall'indice che hai già, una funzionalità conforme allo standard ISO SQL, ma non tutti gli RDBMS lo supportano.
CREATE INDEX vote_order_asc ON big_table (vote, id);
O per ordine decrescente:
SELECT *
FROM big_table
WHERE (vote, id) < (vote_x, id_x) -- ROW values
ORDER BY vote DESC, id DESC
LIMIT n;
Può usare lo stesso indice.
Ti suggerisco di dichiarare le tue colonne NOT NULL
o familiarizzare con NULLS FIRST|LAST
costruire:
- PostgreSQL ordina per datetime asc, null prima?
Nota due cose in particolare:
-
La
ROW
valori nelWHERE
la clausola non può essere sostituita con campi membro separati.WHERE (vote, id) > (vote_x, id_x)
non posso essere sostituito con:WHERE vote >= vote_x AND id > id_xCiò escluderebbe tutto righe con
id <= id_x
, mentre vogliamo farlo solo per lo stesso voto e non per il prossimo. La traduzione corretta sarebbe:WHERE (vote = vote_x AND id > id_x) OR vote > vote_x
... che non funziona altrettanto bene con gli indici e diventa sempre più complicato per più colonne.
Sarebbe semplice per un single colonna, ovviamente. Questo è il caso speciale di cui ho parlato all'inizio.
-
La tecnica non funziona per direzioni miste in
ORDER BY
come:ORDER BY vote ASC, id DESC
Almeno non riesco a pensare a un generico modo per implementarlo nel modo più efficiente. Se almeno una di entrambe le colonne è di tipo numerico, puoi utilizzare un indice funzionale con un valore invertito su
(vote, (id * -1))
- e usa la stessa espressione inORDER BY
:ORDER BY vote ASC, (id * -1) ASC
Correlati:
- Termine della sintassi SQL per 'WHERE (col1, col2) <(val1, val2)'
- Migliora le prestazioni per l'ordine con colonne di molte tabelle
Da notare in particolare la presentazione di Markus Win e ho collegato a:
- "Impaginazione eseguita alla maniera PostgreSQL"