sql >> Databáze >  >> RDS >> PostgreSQL

Optimalizujte dotaz pomocí OFFSET na velké tabulce

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ě:

  1. ROW hodnoty v WHERE 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_x

    To 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.

  2. 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 v ORDER 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"


  1. rozdíl mezi plánem vysvětlit a plánem provedení

  2. Jak vytvořit uložené procedury v SQL?

  3. Jaká je maximální velikost VARCHAR2 v PL/SQL a SQL?

  4. SQLiteException:tabulka již existuje