Оптимизация запроса для двух таблиц с объединением и сортировкой - PullRequest
1 голос
/ 26 марта 2020

Не удалось придумать лучший заголовок для подробного описания проблемы. У меня есть две таблицы core_question и core_team_worker.

core_question имеет два столбца id int4 и team_id int4.

core_team_worker имеет три столбца id int4, worker_id int4, team_id int4.

Таким образом, с помощью этого core_team_worker можно определить отношения между многими работниками и командами.

Теперь у меня есть следующий запрос, который мне нужно оптимизировать, нужно получить один уточняющий вопрос в порядке, полученном для указанного работника.

select q.id
from core_question q
join core_team_worker tw on tw.team_id = q.team_id
where tw.worker_id = 18  -- this is where I put the worker id, let's say 18 for example.
order by q.team_id asc, q.id asc
limit 1;

Теперь с 1,6 миллионами записей я получаю 830 мс на этот запрос. У меня есть следующие индексы баз данных:

У меня есть индексы b-дерева для первичных ключей и внешних ключей id, team_id для core_question и id, team_id, worker_id для core_team_worker. В дополнение к этому я создал многостолбцовый индекс для core_question, чтобы оптимизировать этот запрос, который прекрасно работает:

select q.id from core_question q
where q.team_id = 4
order by q.team_id asc, q.id asc
limit 1;

Вот код для многостолбцового индекса

create index core_question_team_id_idx on core_question (team_id asc nulls last, id asc nulls last);

Теперь, если Я удаляю order by или фильтр на worker_id = 18 Я получаю время отклика 1 мс, что идеально. Если бы я мог создать индекс, включающий обе таблицы, что-то вроде (team_id, worker_id, question_id), которое помогло бы с этим запросом, но я понимаю, что такой индекс невозможен.

Я потратил довольно много времени, пытаясь оптимизировать этот запрос, но я все еще могу никуда не денется Как бы оптимизировать этот запрос?

Вот результат анализа объяснения:

Limit  (cost=0.57..0.69 rows=1 width=8) (actual time=486.687..486.689 rows=1 loops=1)
  ->  Nested Loop  (cost=0.57..53378.10 rows=473106 width=8) (actual time=486.686..486.686 rows=1 loops=1)
        Join Filter: (q.team_id = tw.team_id)
        Rows Removed by Join Filter: 1130118
        ->  Index Only Scan using core_question_team_id_idx on core_question q  (cost=0.43..28924.18 rows=1630117 width=8) (actual time=0.022..150.151 rows=1130119 loops=1)
              Heap Fetches: 0
        ->  Materialize  (cost=0.15..2.17 rows=1 width=4) (actual time=0.000..0.000 rows=1 loops=1130119)
              ->  Index Scan using core_team_worker_worker_id_823a9ecc on core_team_worker tw  (cost=0.15..2.17 rows=1 width=4) (actual time=0.003..0.004 rows=1 loops=1)
                    Index Cond: (worker_id = 18)
Planning Time: 0.374 ms
Execution Time: 486.723 ms

1 Ответ

0 голосов
/ 26 марта 2020

Если у вас мало строк, соответствующих условиям, ваш план выполнения вполне разумный. Вы можете попробовать:

select q.id
from core_question q join
     core_team_worker tw
     on tw.team_id = q.team_id
where tw.worker_id = 18  -- this is where I put the worker id, let's say 18 for example.
order by q.team_id asc, q.id asc
limit 1;

Я бы предложил следующие индексы:

  • core_team_worker(worker_id, team_id)
  • core_team(team_id).

Я не думаю, что есть способ обычно избегать внешнего вида. Но, если на одного работника есть только одна запись, тогда запрос формулируется так:

select q.id
from core_question q join
     core_team_worker tw
     on tw.team_id = q.team_id
where exists (select 1
              from core_team_worker tw
              where t2.team_id = q.team_id and
                    tw.worker_id = 18 
             )
order by q.team_id asc, q.id asc
limit 1;

. Тогда это может использовать индексы core_team_worker(team_id, worker_id) и core_question(team_id, id). Но это, вероятно, вернется в ваш план.

...