sql >> Database teknologi >  >> RDS >> PostgreSQL

Optimer forespørgsel med OFFSET på stort bord

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:

  1. ROW værdier i WHERE 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_x

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

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


  1. Finjustering af Oracle DG40DBC

  2. SQL-forespørgselskolonnen findes ikke fejl

  3. Hvordan bruger jeg den aktuelle dato i en HQL-forespørgsel med en Oracle-database?

  4. Stage APPL_TOP i Oracle Applications R12