Velký OFFSET
bude vždy pomalý. Postgres musí seřadit všechny řádky a spočítat viditelné jedničky až do vašeho offsetu. Chcete-li přeskočit všechny předchozí řádky přímo můžete přidat indexované row_number
do tabulky (nebo vytvořte MATERIALIZED VIEW
včetně uvedeného row_number
) a pracujte s WHERE row_number > x
místo OFFSET x
.
Tento přístup je však smysluplný pouze pro data pouze pro čtení (nebo většinou). Totéž implementujeme pro data tabulky, která se mohou měnit současně je náročnější. Musíte začít tím, že přesně definujete požadované chování .
Navrhuji jiný přístup pro stránkování :
SELECT *
FROM big_table
WHERE (vote, id) > (vote_x, id_x) -- ROW values
ORDER BY vote, id -- needs to be deterministic
LIMIT n;
Kde vote_x
a id_x
jsou z posledního řádku předchozí stránky (pro oba DESC
a ASC
). Nebo od prvního při navigaci zpět .
Porovnávání hodnot řádků je podporováno indexem, který již máte – funkce, která odpovídá standardu ISO SQL, ale ne každý RDBMS ji podporuje.
CREATE INDEX vote_order_asc ON big_table (vote, id);
Nebo pro sestupné pořadí:
SELECT *
FROM big_table
WHERE (vote, id) < (vote_x, id_x) -- ROW values
ORDER BY vote DESC, id DESC
LIMIT n;
Může použít stejný index.
Doporučuji, abyste své sloupce deklarovali NOT NULL
nebo se seznamte s NULLS FIRST|LAST
konstrukce:
- PostgreSQL řazení podle datetime asc, nejprve null?
Všimněte si dvě věcí konkrétně:
-
ROW
hodnoty vWHERE
klauzuli nelze nahradit oddělenými členskými poli.WHERE (vote, id) > (vote_x, id_x)
nelze být nahrazeno:WHERE vote >= vote_x AND id > id_xTo by vyloučilo vše řádky s
id <= id_x
, zatímco my to chceme udělat pouze pro stejný hlas a ne pro další. Správný překlad by byl:WHERE (vote = vote_x AND id > id_x) OR vote > vote_x
... který si s indexy tak hezky nehraje a pro více sloupců je čím dál komplikovanější.
Pro single by to bylo jednoduché sloupec, samozřejmě. To je speciální případ, který jsem zmínil na začátku.
-
Tato technika nefunguje pro smíšené trasy v
ORDER BY
jako:ORDER BY vote ASC, id DESC
Alespoň mě nenapadá generika způsob, jak to implementovat co nejefektivněji. Pokud je alespoň jeden z obou sloupců číselného typu, můžete použít funkční index s obrácenou hodnotou na
(vote, (id * -1))
- a použijte stejný výraz vORDER BY
:ORDER BY vote ASC, (id * -1) ASC
Související:
- Výraz v syntaxi SQL pro „WHERE (sloupec1, sloupec2) <(hodnota1, hodnota2)“
- Zlepšete výkon objednávky pomocí sloupců z mnoha tabulek
Všimněte si zejména prezentace Markuse Winanda, na kterou jsem odkazoval:
- "Paginace provedena způsobem PostgreSQL"