PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

Ottimizza la query con OFFSET su una tabella di grandi dimensioni

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:

  1. La ROW valori nel WHERE 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_x

    Ciò 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.

  2. 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 in ORDER 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"