У меня есть следующий запрос:
SELECT
id,
RANK() OVER (
PARTITION BY user_id
ORDER BY
effective_date <= '${date}' DESC,
effective_date DESC,
created_at DESC
) AS rank
FROM $table WHERE company_id = $companyId AND effective_date <= $date;
Я хочу добавить индекс в таблицу, чтобы ускорить выполнение запроса. Я предполагаю, что наличие составного индекса (company_id, effect_date) поможет. Однако я также хотел добавить столбец user_id как часть индекса.
Итак, мой вопрос: следует ли мне использовать один индекс (company_id, effect_date, user_id)? Поможет ли использование userId в качестве последней части составного индекса производительности PARTITION BY user_id ИЛИ мне следует добавить отдельный индекс только для UserID? Или это не имеет значения, так как это вообще не поможет?
Также стоит упомянуть: есть также несколько запросов, которые не используют ранги и разделы, а фильтрwhere применяется только к Company_id и user_id. В этом случае лучшим вариантом индекса будет (company_id, user_id). Однако в большинстве случаев используется первый запрос, поэтому повышение производительности этого запроса будет наиболее важной задачей.
Вывод EXPLAIN (ANALYZE, VERBOSE, BUFFERS, SETTINGS) при наличии индекса (company_id, effect_date, user_id):
WindowAgg (cost=35.39..36.91 rows=55 width=29) (actual time=0.396..0.596 rows=56 loops=1)
Output: id, rank() OVER (?), ((effective_date <= '2025-05-30'::date)), effective_date, created_at, user_id
Buffers: shared hit=5
-> Sort (cost=35.39..35.53 rows=55 width=21) (actual time=0.342..0.352 rows=56 loops=1)
Output: ((effective_date <= '2025-05-30'::date)), effective_date, created_at, user_id, id
Sort Key: user_role.user_id, ((user_role.effective_date <= '2025-05-30'::date)) DESC, user_role.effective_date DESC, user_role.created_at DESC
Sort Method: quicksort Memory: 29kB
Buffers: shared hit=5
-> Bitmap Heap Scan on public.user_role (cost=4.84..33.80 rows=55 width=21) (actual time=0.223..0.273 rows=56 loops=1)
Output: (effective_date <= '2025-05-30'::date), effective_date, created_at, user_id, id
Recheck Cond: ((user_role.company_id = 47) AND (user_role.effective_date <= '2025-05-30'::date))
Heap Blocks: exact=3
Buffers: shared hit=5
-> Bitmap Index Scan on user_role__company_id_effective_date_user_id_idx (cost=0.00..4.83 rows=55 width=0) (actual time=0.170..0.171 rows=56 loops=1)
Index Cond: ((user_role.company_id = 47) AND (user_role.effective_date <= '2025-05-30'::date))
Buffers: shared hit=2
Settings: jit = 'off'
Planning Time: 0.959 ms
Execution Time: 0.785 ms
@user1191247 user1191247 да, я протестировал несколько вариантов, во всех случаях я получаю одинаковую производительность, и когда я запускаю запрос с анализом объяснения, я всегда получаю: Ключ сортировки: user_id, ((efficient_date <= '2024-05-30':: date)) DESC, effect_date DESC, create_at DESC" и метод сортировки: быстрая сортировка Память: 29 КБ" фильтрация в предложенииwhere всегда одинакова. Однако я не знаю, есть ли какая-либо польза от наличия user_id в индексе. Также может случиться так, что моя выборка данных недостаточно велика, чтобы протестировать запросы и увидеть разницу в производительности.
Не могли бы вы поделиться результатами explain(analyze, verbose, buffers, settings) по этому запросу? Обычным текстом.
@FrankHeikens Я обновил описание, указав результаты запроса.
Execution Time: 0.785 ms, это будет близко к совершенству! На данный момент я бы не стал тратить больше времени на этот запрос, он быстрый и, скорее всего, на данный момент достаточно быстрый.
Он отмечен [mysql]; либо удалите этот тег, либо добавьте EXPLAIN (и т. д.) для этой СУБД.
@RickJames, заголовок: «Каков наилучший подход к индексированию в Postgres (или любой другой базе данных SQL)». Меня больше интересует знание того, как индекс работает в таком запросе независимо от БД, а не знание конкретного подхода postgres.
Вполне вероятно, что между различными СУБД существуют несовместимые различия в реализации. Я думаю, что это лучше всего.






Нет, добавление user_id в индекс не ускорит этот конкретный запрос.
Поскольку effective_date сравнивается с <, результат сканирования индекса не будет предварительно упорядочен user_id, поэтому оценка оконной функции не принесет пользы. Дополнительный столбец сделает индекс больше и, следовательно, менее эффективным, поэтому, если вы не можете использовать третий столбец индекса для какого-либо другого запроса, не добавляйте его.
Пробовали ли вы добавлять разные индексы, а затем проверять план выполнения запроса для каждого из них? Это отличный способ исследовать и учиться. Если вы столкнетесь с поведением, которое отличается от того, что вы ожидаете, вы можете задать гораздо более конкретный вопрос.