Голям 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, първо нула?
Забележете две неща по-специално:
-
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
... което не работи толкова добре с индексите и става все по-сложно за повече колони.
Би било просто за сингъл колона, очевидно. Това е специалният случай, който споменах в началото.
-
Техниката не работи за смесени посоки в
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“