Индексы и планы запросов — Примеры

Пример 1 — Поиск вместо сканирования

Список заказов фильтруется по клиенту и сортируется по дате, и по мере роста таблицы он становится медленнее; план ниже — то, что движок выбрал для этого запроса. Определите способ доступа, напишите индекс, который его заменит, и скажите, как вы докажете изменение.

select id, total from orders
where customer_id = 7 order by placed_at desc limit 20;

QUERY PLAN                                        (пояснительно)
 Sort  (rows=4200000)
   ->  Seq Scan on orders  (rows=4200000)
         Filter: (customer_id = 7)
         Rows Removed by Filter: 4199994

Решение

create index orders_customer_placed_idx
  on orders (customer_id, placed_at desc);

-- сначала равенство по customer_id, затем столбец, по которому идёт сортировка

Движок прочитал каждую строку и отбросил почти все. Однако индекс держит заказы одного клиента именно в том порядке, который просит запрос: он переходит к этому клиенту и читает двадцать строк, и этап сортировки исчезает вместе с ними. Доказывать изменение лучше сравнением числа прочитанных строк до и после, а не затраченным временем — быстрая машина скрывает сканирование большой таблицы. Обычно же ошибаются, добавляя индекс и оставляя рядом другой, которым не может воспользоваться ни один запрос: каждая запись от этого дорожает.

Пример 2 — Индекс в неверном порядке

Для экрана заказов добавили составной индекс, а отчёт по диапазону дат всё равно сканирует всю таблицу. Напишите индекс, которым отчёты действительно смогут воспользоваться, и скажите, что произойдёт с тем, который добавили.

create index orders_status_placed_idx on orders (status, placed_at);

-- the report:
select id, total from orders
where placed_at >= date '2026-01-01'
order by placed_at;

Решение

create index orders_placed_idx on orders (placed_at);

drop index orders_status_placed_idx;   -- после проверки, что им никто не пользуется

Индекс по двум столбцам в таком порядке обслуживает фильтр по первому столбцу или по обоим вместе, но на фильтр отчёта по второму в одиночку он эффективно ответить не может. К тому же у столбца status мало различных значений, так что поиск по нему всё равно вернул бы большую долю таблицы. Проверять, какие индексы ещё используются, и убирать лишние так же полезно, как добавлять тот, которого не хватает, — каждый индекс замедляет каждую вставку. Обычно же ошибаются, добавляя индекс за индексом, пока планировщику не из чего выбирать: это стоит записи и даёт ложное чувство безопасности.