En stor OFFSET
vil altid være langsom. Postgres skal bestille alle rækker og tælle de synlige dem op til din offset. For at springe alle tidligere rækker direkte over du kan tilføje et indekseret row_number
til tabellen (eller opret en MATERIALIZED VIEW
inklusive nævnte row_number
) og arbejd med WHERE row_number > x
i stedet for OFFSET x
.
Denne tilgang er dog kun fornuftig for skrivebeskyttede (eller for det meste) data. Implementering af det samme for tabeldata, der kan ændres samtidigt er mere udfordrende. Du skal starte med at definere ønsket adfærd præcist .
Jeg foreslår en anden tilgang til paginering :
SELECT *
FROM big_table
WHERE (vote, id) > (vote_x, id_x) -- ROW values
ORDER BY vote, id -- needs to be deterministic
LIMIT n;
Hvor vote_x
og id_x
er fra sidste række på forrige side (for begge DESC
og ASC
). Eller fra den første hvis du navigerer baglæns .
Sammenligning af rækkeværdier understøttes af det indeks, du allerede har - en funktion, der overholder ISO SQL-standarden, men ikke alle RDBMS understøtter det.
CREATE INDEX vote_order_asc ON big_table (vote, id);
Eller for faldende rækkefølge:
SELECT *
FROM big_table
WHERE (vote, id) < (vote_x, id_x) -- ROW values
ORDER BY vote DESC, id DESC
LIMIT n;
Kan bruge det samme indeks.
Jeg foreslår, at du erklærer dine kolonner NOT NULL
eller gør dig bekendt med NULLS FIRST|LAST
konstruere:
- PostgreSQL sorteres efter datetime asc, null først?
Bemærk to ting især:
-
ROW
værdier iWHERE
klausul kan ikke erstattes med adskilte medlemsfelter.WHERE (vote, id) > (vote_x, id_x)
kan ikke erstattes med:WHERE vote >= vote_x AND id > id_xDet ville udelukke alt rækker med
id <= id_x
, mens vi kun vil gøre det for den samme afstemning og ikke til den næste. Den korrekte oversættelse ville være:WHERE (vote = vote_x AND id > id_x) OR vote > vote_x
... som ikke spiller så godt sammen med indekser og bliver mere og mere kompliceret for flere kolonner.
Ville være enkelt for en enkelt kolonne, selvfølgelig. Det er det særlige tilfælde, jeg nævnte i begyndelsen.
-
Teknikken virker ikke for blandede retninger i
ORDER BY
som:ORDER BY vote ASC, id DESC
Jeg kan i hvert fald ikke komme i tanke om en generisk måde at implementere dette så effektivt på. Hvis mindst en af begge kolonner er en numerisk type, kan du bruge et funktionelt indeks med en inverteret værdi på
(vote, (id * -1))
- og brug det samme udtryk iORDER BY
:ORDER BY vote ASC, (id * -1) ASC
Relateret:
- SQL-syntaksudtryk for 'WHERE (col1, col2) <(val1, val2)'
- Forbedre ydeevnen for ordre efter med kolonner fra mange tabeller
Bemærk især præsentationen af Markus Winand, jeg linkede til:
- "Søgning udført på PostgreSQL-måden"