PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

Zoptymalizuj zapytanie z PRZESUNIĘCIEM na dużym stole

Duże OFFSET zawsze będzie powolny. Postgres musi uporządkować wszystkie rzędy i policzyć widoczne te do twojego offsetu. Aby pominąć wszystkie poprzednie wiersze bezpośrednio możesz dodać zindeksowany row_number do tabeli (lub utwórz MATERIALIZED VIEW w tym wspomniany row_number ) i pracuj z WHERE row_number > x zamiast OFFSET x .

Jednak to podejście jest sensowne tylko w przypadku danych tylko do odczytu (lub w większości). Implementacja tego samego dla danych tabeli, które mogą się zmieniać równocześnie jest trudniejsze. Musisz zacząć od zdefiniowania pożądanego zachowania dokładnie .

Proponuję inne podejście do paginacji :

SELECT *
FROM   big_table
WHERE  (vote, id) > (vote_x, id_x)  -- ROW values
ORDER  BY vote, id  -- needs to be deterministic
LIMIT  n;

Gdzie vote_x i id_x pochodzą od ostatnich wiersz poprzedniej strony (dla obu DESC i ASC ). Lub od pierwszego jeśli nawigujesz wstecz .

Porównywanie wartości wierszy jest obsługiwane przez indeks, który już posiadasz — funkcja, która jest zgodna ze standardem ISO SQL, ale nie każdy RDBMS ją obsługuje.

CREATE INDEX vote_order_asc ON big_table (vote, id);

Lub w porządku malejącym:

SELECT *
FROM   big_table
WHERE  (vote, id) < (vote_x, id_x)  -- ROW values
ORDER  BY vote DESC, id DESC
LIMIT  n;

Może używać tego samego indeksu.
Proponuję zadeklarować kolumny NOT NULL lub zapoznaj się z NULLS FIRST|LAST konstrukcja:

  • PostgreSQL sortuje według daty i godziny asc, najpierw null?

Zwróć uwagę na dwie rzeczy w szczególności:

  1. ROW wartości w WHERE klauzuli nie można zastąpić oddzielnymi polami składowymi. WHERE (vote, id) > (vote_x, id_x) nie można być zastąpione przez:

    WHERE  vote >= vote_x
    AND    id   > id_x

    To wykluczyłoby wszystkie wiersze z id <= id_x , podczas gdy chcemy to zrobić tylko dla tego samego głosowania, a nie dla następnego. Prawidłowe tłumaczenie to:

    WHERE (vote = vote_x AND id > id_x) OR vote > vote_x
    

    ... co nie działa tak dobrze z indeksami i staje się coraz bardziej skomplikowane dla większej liczby kolumn.

    Byłoby proste dla singla kolumna, oczywiście. To szczególny przypadek, o którym wspomniałem na początku.

  2. Technika nie działa dla mieszanych kierunków w ORDER BY jak:

    ORDER  BY vote ASC, id DESC
    

    Przynajmniej nie przychodzi mi do głowy ogólny sposób, aby wdrożyć to równie skutecznie. Jeśli przynajmniej jedna z obu kolumn jest typu liczbowego, możesz użyć indeksu funkcjonalnego z odwróconą wartością (vote, (id * -1)) - i użyj tego samego wyrażenia w ORDER BY :

    ORDER  BY vote ASC, (id * -1) ASC
    

Powiązane:

  • Termin składni SQL dla „GDZIE (col1, col2) <(val1, val2)”
  • Popraw wydajność zamówień dzięki kolumnom z wielu tabel

Zwróć uwagę w szczególności na prezentację Markusa Winanda, do której podałem link:

  • „Paginacja na sposób PostgreSQL”


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Jak zbiorczo zaktualizować identyfikator sekwencji postgreSQL dla wszystkich tabel?

  2. Jak zmienić użytkownika na superużytkownika w PostgreSQL?

  3. Zapytanie krzyżowe PostgreSQL

  4. Łączenie instrukcji INSERT w modyfikującym dane CTE z wyrażeniem CASE

  5. Jak rzutować ciąg na liczbę całkowitą i mieć 0 w przypadku błędu rzutowania za pomocą PostgreSQL?