#sql #postgresql
Вопрос:
У меня есть 2 таблицы : Звонки (10 000 строк) , CRM (25 миллионов строк) Я хочу совершать звонки, чтобы присоединиться к CRM.
select *
from calls a
left join crm b
on (
(a.customerID = b.customerID)
OR
(a.Number1 in (b.Number_A,b.Number_B))
OR
(a.Number2 in (b.Number_A,b.Number_B))
);
Когда я просто соединяю идентификатор клиента, он работает нормально. Но приведенный выше код вызывает тайм-аут, и он выходит из строя.
Комментарии:
1. Вы должны, по крайней мере, добавить вывод ОБЪЯСНЕНИЯ (т. Е. Выполнить
EXPLAIN select * from ...
) в свой вопрос и описать имеющиеся у вас индексы. У вас есть индексы, например, для b.number_a и b.number_b?2. У меня есть индексы по всем столбцам соединения.
3. Ваш запрос не содержит условий фильтрации и извлекает все комбинации строк, соответствующие условию соединения. Вы уверены, что хотите просмотреть всю таблицу целиком?
4. Помогут примеры данных и желаемые результаты.
5. В вашем запросе может быть до 250 миллиардов строк результатов. Сколько вы ожидаете? Если текущий план объяснения ожидает очень много совпадений, и вы знаете, что их будет всего несколько, то установка
pg_hint_plan
и добавление подсказки строк в запрос может очень помочь.
Ответ №1:
Я бы предложил несколько left join
s:
select c.*,
coalesce(cc.col1, c1a.col1, c1b.col1, c2a.col1, c2b.col1)
from calls c left join
crm cc
on c.customerID = cc.customerID left join
crm c1a
on c.Number1 = c1a.Number_A left join
crm c1b
on c.Number1 = c1b.Number_B left join
crm c2a
on c.Number2 = c2a.Number_A left join
crm c2b
on c.Number2 = c2b.Number_B;
Затем это может использовать преимущества индексов на crm(CustomerId)
, crm(Number1)
, и crm(Number2)
.
Комментарии:
1. Не вызовет ли это дупса? У меня есть индексы для всех столбцов соединения
2. Я не думаю, что это сработает. Если у вызова есть одна строка crm, соответствующая идентификатору клиента, и другая строка crm, соответствующая номеру 1, то мы получаем только одну строку результата, и
COALESCE
только она показывает нам одно из двух значений col1.3. @SaadAmir . . . Это зависит от того, что вы подразумеваете под «двойниками».
Ответ №2:
Иногда при замене одного запроса, содержащего два условия , OR
на два запроса, которые склеиваются вместе UNION
, это приводит к улучшению плана выполнения. Я никогда не понимал, почему оптимизаторы СУБД сами не принимают это во внимание. И я не знаю, верно ли это для PostgreSQL или нет. Но, возможно, стоит попробовать.
В вашем случае в запросе есть внешнее соединение. Это усложняет дело. С помощью отдельных запросов мы можем получить как внешние объединенные, так и совпадающие строки crm для вызова и в этом случае должны избавиться от первых.
select *
from
(
select * from calls left join crm on crm.customerID = calls.customerID
union
select * from calls left join crm on crm.number_a = calls.number1
union
select * from calls left join crm on crm.number_a = calls.number2
union
select * from calls left join crm on crm.number_b = calls.number1
union
select * from calls left join crm on crm.number_b = calls.number2
) data
order by rank() over (partition by calls.id order by case when crm.id is null then 2 else 1 end)
fetch first row with ties;
Чтобы это работало быстро, у вас должен быть один индекс на столбец в запросе, т. е. Шесть индексов с одним столбцом.
Будет ли это быстрее, чем ваш исходный запрос, зависит от многих вещей. Главным образом: чем меньше совпадений, тем лучше.
Комментарии:
1. Просто примечание:
UNION
может быть намного дороже, чемUNION ALL
.2. @Цепеш: Да, я имел это в виду. Но мне пришлось избавиться от повторяющихся совпадений и повторяющихся несоответствий перед подачей заявки
RANK
. Я не знаю, будет ли иметь значение, если начать с »UNION ALL
вместо», а затем применитьDISTINCT
позже. Может быть, можно попробовать другой вариант.