Een grote OFFSET
gaat altijd langzaam. Postgres moet alle rijen ordenen en de zichtbare . tellen degenen tot uw offset. Alle vorige rijen direct overslaan je zou een geïndexeerd row_number
kunnen toevoegen naar de tafel (of maak een MATERIALIZED VIEW
inclusief genoemd row_number
) en werk met WHERE row_number > x
in plaats van OFFSET x
.
Deze benadering is echter alleen zinvol voor alleen-lezen (of meestal) gegevens. Hetzelfde implementeren voor tabelgegevens die gelijktijdig kunnen veranderen is uitdagender. U moet beginnen met het definiëren van gewenst gedrag precies .
Ik stel een andere benadering voor voor paginering :
SELECT *
FROM big_table
WHERE (vote, id) > (vote_x, id_x) -- ROW values
ORDER BY vote, id -- needs to be deterministic
LIMIT n;
Waar vote_x
en id_x
zijn van de laatste rij van de vorige pagina (voor beide DESC
en ASC
). Of vanaf de eerste als u achteruit navigeert .
Het vergelijken van rijwaarden wordt ondersteund door de index die u al heeft - een functie die voldoet aan de ISO SQL-standaard, maar niet elk RDBMS ondersteunt dit.
CREATE INDEX vote_order_asc ON big_table (vote, id);
Of voor aflopende volgorde:
SELECT *
FROM big_table
WHERE (vote, id) < (vote_x, id_x) -- ROW values
ORDER BY vote DESC, id DESC
LIMIT n;
Kan dezelfde index gebruiken.
Ik raad u aan uw kolommen NOT NULL
te declareren of maak kennis met de NULLS FIRST|LAST
constructie:
- PostgreSQL sorteren op datum/tijd asc, eerst null?
Let op twee dingen in het bijzonder:
-
De
ROW
waarden in deWHERE
clausule kan niet worden vervangen door gescheiden lidvelden.WHERE (vote, id) > (vote_x, id_x)
kan niet worden vervangen door:WHERE vote >= vote_x AND id > id_xDat zou alles uitsluiten rijen met
id <= id_x
, terwijl we dat alleen voor dezelfde stemming willen doen en niet voor de volgende. De juiste vertaling zou zijn:WHERE (vote = vote_x AND id > id_x) OR vote > vote_x
... die niet zo goed samengaat met indexen, en steeds ingewikkelder wordt voor meer kolommen.
Zou eenvoudig zijn voor een single kolom, uiteraard. Dat is het speciale geval dat ik in het begin noemde.
-
De techniek werkt niet voor gemengde richtingen in
ORDER BY
zoals:ORDER BY vote ASC, id DESC
Ik kan tenminste geen generiek bedenken manier om dit zo efficiënt mogelijk te implementeren. Als ten minste één van beide kolommen een numeriek type is, kunt u een functionele index gebruiken met een omgekeerde waarde op
(vote, (id * -1))
- en gebruik dezelfde uitdrukking inORDER BY
:ORDER BY vote ASC, (id * -1) ASC
Gerelateerd:
- SQL-syntaxisterm voor 'WHERE (col1, col2) <(val1, val2)'
- Verbeter de prestaties voor volgorde door met kolommen uit veel tabellen
Let in het bijzonder op de presentatie van Markus Winand waarnaar ik heb gelinkt:
- "Paginering gedaan op de PostgreSQL-manier"