PostgreSQL
 sql >> база данни >  >> RDS >> PostgreSQL

Оптимизирайте заявката с OFFSET върху голяма таблица

Голям OFFSET винаги ще бъде бавно. Postgres трябва да подреди всички редове и да брои видимите такива до вашия офсет. За да пропуснете всички предишни редове директно можете да добавите индексиран row_number към таблицата (или създайте MATERIALIZED VIEW включително споменатия row_number ) и работи с WHERE row_number > x вместо OFFSET x .

Този подход обаче е разумен само за данни само за четене (или предимно). Прилагане на същото за данни от таблицата, които могат да се променят едновременно е по-предизвикателно. Трябва да започнете с дефиниране на желаното поведение точно .

Предлагам различен подход запагинация :

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

Където vote_x и id_x са от последните ред на предишната страница (и за двата DESC и ASC ). Или от първия ако навигирате назад .

Сравняването на стойности на редове се поддържа от индекса, който вече имате – функция, която е в съответствие със стандарта ISO SQL, но не всяка RDBMS я поддържа.

CREATE INDEX vote_order_asc ON big_table (vote, id);

Или за низходящ ред:

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

Може да използва същия индекс.
Предлагам ви да декларирате колоните си NOT NULL или се запознайте с NULLS FIRST|LAST конструкция:

  • PostgreSQL сортиране по datetime asc, първо нула?

Забележете две неща по-специално:

  1. ROW стойности в WHERE клаузата не може да бъде заменена с отделни полета за член. WHERE (vote, id) > (vote_x, id_x) не може да бъде заменен с:

    WHERE  vote >= vote_x
    AND    id   > id_x

    Това би изключило всички редове с id <= id_x , докато искаме да направим това само за същия вот, а не за следващия. Правилният превод би бил:

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

    ... което не работи толкова добре с индексите и става все по-сложно за повече колони.

    Би било просто за сингъл колона, очевидно. Това е специалният случай, който споменах в началото.

  2. Техниката не работи за смесени посоки в ORDER BY като:

    ORDER  BY vote ASC, id DESC
    

    Поне не се сещам за генерик начин да приложите това възможно най-ефективно. Ако поне една от двете колони е числов тип, можете да използвате функционален индекс с обърната стойност на (vote, (id * -1)) - и използвайте същия израз в ORDER BY :

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

Свързано:

  • SQL синтактичен термин за „WHERE (col1, col2) <(val1, val2)“
  • Подобрете производителността за подреждане по с колони от много таблици

Обърнете внимание по-специално на презентацията на Маркус Уинанд, към която имам връзка:

  • „Пагинацията е извършена по начина на 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. Има ли пряк път за SELECT * FROM?

  2. Възстановяване на архивен файл на postgres с помощта на командния ред?

  3. Как да превключите база данни с помощта на PostgreSQL

  4. Вмъкнете данни и задайте външни ключове с Postgres

  5. Как да създадете обобщена таблица в PostgreSQL