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:
-
Die
ROW
Werte inWHERE
-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_xDas 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.
-
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 inORDER 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"