En stor OFFSET
kommer alltid att gå långsamt. Postgres måste beställa alla rader och räkna de synliga upp till din offset. För att hoppa över alla tidigare rader direkt du kan lägga till ett indexerat row_number
till tabellen (eller skapa en MATERIALIZED VIEW
inklusive nämnda row_number
) och arbeta med WHERE row_number > x
istället för OFFSET x
.
Detta tillvägagångssätt är dock bara förnuftigt för skrivskyddad (eller mestadels) data. Implementerar detsamma för tabelldata som kan ändras samtidigt är mer utmanande. Du måste börja med att definiera önskat beteende exakt .
Jag föreslår ett annat tillvägagångssätt för paginering :
SELECT *
FROM big_table
WHERE (vote, id) > (vote_x, id_x) -- ROW values
ORDER BY vote, id -- needs to be deterministic
LIMIT n;
Där vote_x
och id_x
är från förra raden på föregående sida (för båda DESC
och ASC
). Eller från den första om du navigerar bakåt .
Att jämföra radvärden stöds av det index du redan har - en funktion som följer ISO SQL-standarden, men inte alla RDBMS stöder den.
CREATE INDEX vote_order_asc ON big_table (vote, id);
Eller för fallande ordning:
SELECT *
FROM big_table
WHERE (vote, id) < (vote_x, id_x) -- ROW values
ORDER BY vote DESC, id DESC
LIMIT n;
Kan använda samma index.
Jag föreslår att du deklarerar dina kolumner NOT NULL
eller bekanta dig med NULLS FIRST|LAST
konstruera:
- PostgreSQL sortera efter datetime asc, null först?
Notera två saker särskilt:
-
ROW
värden iWHERE
klausul kan inte ersättas med separerade medlemsfält.WHERE (vote, id) > (vote_x, id_x)
kan inte ersättas med:WHERE vote >= vote_x AND id > id_xDet skulle utesluta alla rader med
id <= id_x
, medan vi bara vill göra det för samma omröstning och inte för nästa. Den korrekta översättningen skulle vara:WHERE (vote = vote_x AND id > id_x) OR vote > vote_x
... som inte spelar lika bra med index och blir allt mer komplicerat för fler kolumner.
Skulle vara enkelt för en singel kolumn, uppenbarligen. Det är det speciella fallet jag nämnde i början.
-
Tekniken fungerar inte för blandade vägbeskrivningar i
ORDER BY
gillar:ORDER BY vote ASC, id DESC
Jag kan åtminstone inte komma på en generisk sätt att implementera detta så effektivt. Om minst en av båda kolumnerna är en numerisk typ, kan du använda ett funktionsindex med ett inverterat värde på
(vote, (id * -1))
- och använd samma uttryck iORDER BY
:ORDER BY vote ASC, (id * -1) ASC
Relaterat:
- SQL-syntaxterm för 'WHERE (col1, col2) <(val1, val2)'
- Förbättra prestandan för ordning efter med kolumner från många tabeller
Notera särskilt presentationen av Markus Winand jag länkade till:
- "Sökning utförd på PostgreSQL-sättet"