PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Optimieren Sie die Abfrage mit OFFSET für große Tabellen

Ein großer OFFSET wird immer langsam sein. Postgres muss alle Zeilen ordnen und die sichtbaren zählen diejenigen bis zu Ihrem Offset. Um alle vorherigen Zeilen direkt zu überspringen Sie könnten eine indizierte row_number hinzufügen in die Tabelle (oder erstellen Sie eine MATERIALIZED VIEW einschließlich besagter row_number ) und arbeite mit WHERE row_number > x statt OFFSET x .

Dieser Ansatz ist jedoch nur für schreibgeschützte (oder überwiegend) Daten sinnvoll. Implementieren Sie dasselbe für Tabellendaten, die sich gleichzeitig ändern können ist anspruchsvoller. Sie müssen damit beginnen, das gewünschte Verhalten exakt zu definieren .

Ich schlage einen anderen Ansatz für die Paginierung vor :

SELECT *
FROM   big_table
WHERE  (vote, id) > (vote_x, id_x)  -- ROW values
ORDER  BY vote, id  -- needs to be deterministic
LIMIT  n;

Wobei vote_x und id_x sind vom letzten Zeile der vorherigen Seite (für sowohl DESC und ASC ). Oder von der ersten wenn Sie rückwärts navigieren .

Das Vergleichen von Zeilenwerten wird durch den bereits vorhandenen Index unterstützt - eine Funktion, die dem ISO-SQL-Standard entspricht, aber nicht von jedem RDBMS unterstützt wird.

CREATE INDEX vote_order_asc ON big_table (vote, id);

Oder für absteigende Reihenfolge:

SELECT *
FROM   big_table
WHERE  (vote, id) < (vote_x, id_x)  -- ROW values
ORDER  BY vote DESC, id DESC
LIMIT  n;

Kann denselben Index verwenden.
Ich schlage vor, Sie deklarieren Ihre Spalten NOT NULL oder machen Sie sich mit NULLS FIRST|LAST vertraut konstruieren:

  • PostgreSQL sortiert nach datetime asc, null zuerst?

Beachten Sie zwei Dinge insbesondere:

  1. Die ROW Werte in WHERE -Klausel kann nicht durch getrennte Mitgliedsfelder ersetzt werden. WHERE (vote, id) > (vote_x, id_x) kann nicht ersetzt werden durch:

    WHERE  vote >= vote_x
    AND    id   > id_x

    Das würde alle ausschließen Zeilen mit id <= id_x , während wir das nur für dieselbe Abstimmung tun wollen und nicht für die nächste. Die korrekte Übersetzung wäre:

    WHERE (vote = vote_x AND id > id_x) OR vote > vote_x
    

    ... was mit Indizes nicht so gut mitspielt und bei mehr Spalten immer komplizierter wird.

    Wäre für eine Single einfach Spalte, offensichtlich. Das ist der Spezialfall, den ich eingangs erwähnt habe.

  2. Die Technik funktioniert nicht für gemischte Richtungen in ORDER BY wie:

    ORDER  BY vote ASC, id DESC
    

    Zumindest fällt mir kein Generikum ein Weg, dies möglichst effizient umzusetzen. Wenn mindestens eine der beiden Spalten ein numerischer Typ ist, könnten Sie einen funktionalen Index mit einem invertierten Wert auf (vote, (id * -1)) verwenden - und verwenden Sie denselben Ausdruck in ORDER BY :

    ORDER  BY vote ASC, (id * -1) ASC
    

Verwandte:

  • SQL-Syntaxbegriff für 'WHERE (col1, col2) <(val1, val2)'
  • Verbessern Sie die Leistung für sortieren nach mit Spalten aus vielen Tabellen

Beachten Sie insbesondere die Präsentation von Markus Winand, die ich verlinkt habe:

  • "Paginierung auf PostgreSQL-Art"