sql >> Databasteknik >  >> RDS >> PostgreSQL

Optimera sökfrågan med OFFSET på stort bord

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:

  1. ROW värden i WHERE 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_x

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

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


  1. Så här konfigurerar du kluster-till-klusterreplikering för PostgreSQL

  2. MariaDB JSON_MERGE_PRESERVE() Förklarad

  3. DATE_ADD() Exempel – MySQL

  4. postgreSQL - in vs any