2014-01-17 4 views
2

мне нужно оптимизировать запрос, в котором присоединиться зависит от состоянияКак оптимизировать запрос с условным соединением?

У меня есть две таблицы

Table1 имеет две колонки, назовём их А и столбцы B, которые могут коснуться столбца table2 C,

если столбец B имеет нулевое значение, я должен соответствовать T1.a с t2.C если столбец B не равно нулю, я должен соответствовать T1.b с t2.C

и, наконец, я должен знать, какие записи на t1 не соответствуют по t2 ...

Чтобы предоставить более подробную информацию, t1 представляет собой таблицу клиентов, и оба A и B являются клиентскими кодами. Коды никогда не совпадают с кодами B, а в случаях, когда существует B, B имеет приоритет (B - это новый клиентский код, но старые клиенты его не имеют. В случае, когда B является нулевым, A является код для использования) (все это, потому что столбец B является новым, а старые клиенты имеют нулевые значения для B).

t2 - таблица покупок. t2.C - это клиентский код, но в этом случае это один столбец, он хранит коды для старых клиентов и коды B для новых клиентов.

Единственное, что я хочу, это знать, у каких клиентов пока нет покупок, с максимально эффективным запросом.

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

Первая попытка:

select * 
from t1 
left join t2 on (t1.A = t2.C or t1.B = t2.C) 
where t2.D is null; 

Обратите внимание, что я могу использовать OR потому что я ЗНАЮ, что t1.A никогда не будет таким же, как любой t1.B, поэтому в t2.C, он coult будет соответствовать только A или B, но никогда не будет (предположим, что условие гарантировано). Запрос настолько медленный, что он был отключен в моем SQL-клиенте.

Вторая попытка

select * 
from t1 
left join t2 on (if(t1.B is null, t1.A = t2.C, t1.B = t2.C)) 
where t2.D is null; 

В этом случае условие Comparision зависит от T1.b, если равно нулю, он сравнивает с A, и если это не так, с Б. Снова запрос крайне медленно ,

Я думаю, я мог бы использовать только два соединения и использовать каждое условие (A или B) для каждого соединения, но я точно не знаю, как его достичь, особенно потому, что мне нужно получить только те случаи, когда ни один A o B имеют совпадение по t2. (т. е. клиенты t1 без покупки t2)

Каковы мои возможности для создания более эффективных запросов для такого случая?

Благодаря

ответ

2

Если у вас нет индексов на t1.A или t1.B то я подозреваю IFNULL будет вашим лучшим выбором:

select * 
from t1 
left join t2 on ifnull(t1.B, t1.A) = t2.C 
where t2.D is null; 

Однако, если один столбец индексируется, я подозреваю, что вы получите лучшую производительность, используя UNION ALL:

select * 
from t1 
left join t2 on t1.A = t2.C 
where t2.D is null 
and t1.B is null 
union all 
select * 
from t1 
left join t2 on t1.B = t2.C 
where t2.D is null 
and t1.B is not null; 

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

Example on SQL Fiddle

+0

спасибо! первая версия (с 'ifnull') работает очень хорошо, то, что я вижу, очень похоже на мою вторую попытку, в которой я использую' on (if (t1.B is null, t1.A = t2.C, t1.B = t2.C)) 'вместо вашего' on ifnull (t1.B, t1.A) = t2.C'. Что делает их настолько разными, что ваша версия более эффективна, чем моя? Я нахожу это действительно интересным. И действительно, ваша версия ifnull работает быстрее, чем объединение всех (.2 против 6 секунд), несмотря на то, что у меня есть индексы для всех задействованных столбцов (A, B и C). – DiegoDD

+0

Причина 'IFNULL' работает лучше, чем ваш оператор if, потому что столбец' t2.C' не содержится в нем, он статичен, поэтому MySQL знает, что он может использовать индекс в этом столбце, тогда как в вашем if-заявлении он не может работать, что 't2.C' используется как в истинном, так и в ложном выражениях, поэтому он не использует индекс. – GarethD

+0

О, теперь я понял! поэтому это происходит не из-за просто использования 'ifnull (x, y)' вместо 'if (x is null, y, x)', а потому, что столбец сравнения находится за пределами 'if'. Поэтому, используя '(if (t1.B is null, t1.A, t1.B) = t2.C)' - это то же самое, что и с ifnull(). Благодаря! – DiegoDD

1

Увы, делает условную присоединяется, как правило, приводит к очень низкой производительности запросов. В этом случае вы тестируете два значения и, по-видимому, смотрите, не существует ли один из них. Попробуйте разбить это на два соединения:

select * 
from t1 left join 
    t2 
    on t1.A = t2.C left join 
    t2 t2a 
    on t1.B = t2a.C 
where t2.D is null and t2a.D is null; 

Это позволит использовать индексы на A, B и C.

Смежные вопросы