2013-06-28 8 views
0

Следующий запрос быстр:Почему «присоединяться» или «гораздо медленнее два» присоединяться?

SELECT * 
FROM apple 
     LEFT JOIN banana b1 
       ON apple.id = b1.one 
     LEFT JOIN banana b2 
       ON apple.id = b2.two 
WHERE b1.id IS NULL 
     AND b2.is IS NULL 

Хотя нижеследующее медленно:

SELECT * 
FROM apple 
     LEFT JOIN banana 
       ON apple.id = banana.one 
        OR apple.id = banana.two 
WHERE banana.id IS NULL 

Может кто-нибудь объяснить, почему делать «присоединиться на» заявление с «или» так гораздо медленнее, чем вступление против двух таблиц?

+1

«так медленнее»? Насколько медленнее? –

+0

Что это за база данных и что показывает план объяснения? – StilesCrisis

+0

«быстрый» «медленный» Ах, да, эти полезные и точные технические показатели! –

ответ

5

В первом запросе при подключении mysql будет искать только один столбец из таблицы banana (N look-ups, где N - nb записей из таблицы apple).

Во втором запросе он должен будет сделать взгляд вверх, используя 2 колонки из banana таблицы еще более в худшем случае это придется делать NxN внешнего вид окна, где N является пью записей из apple

Вы можете узнать больше об используемых алгоритмах here.

Также вы можете проверить EXPLAIN выход в этом fiddle предоставленный Stan McGeek

UPDATE: также иметь в виду:

Если вы используете LEFT JOIN, чтобы найти строки, которые не существуют в некоторой таблице и у вас есть следующий тест: col_name IS NULL в части WHERE, где col_name - это столбец, объявленный как NOT NULL, MySQL останавливается search для большего количества строк (для конкретной комбинации клавиш) после него нашел одну строку, которая соответствует условию LEFT JOIN.

+0

Возможно, это тоже будет полезно: http://sqlfiddle.com/#!2/f5767/6 –

+0

@StanMcGeek thx Я добавил его в ответ – Stephan

+0

Не понимаю, почему запрос «OR» потребует N x N. Я ожидал бы худший случай 2N, а не n^2. – alzaimar

2

Эта удивительная ситуация возникает из-за того, что оператор OR объединяет два столбца, тем самым предотвращая использование любого индекса в любом столбце.

Предположим, что банан имеет два индекса, один на banana.one и еще один на banana.two.

В первом запросе оптимизатор сможет использовать индекс для каждого отдельного JOIN, поскольку они выполняются в двух разных проходах. Каждый JOIN будет использовать один из двух индексов на banana (сложность = Nx2 = N, где N = количество яблок).

Во второй версии есть только один JOIN и один раз. Но JOIN может использовать только один индекс. Поскольку ни один из индексов недостаточен (учитывается только один из двух условий JOIN), он не будет использовать какой-либо индекс и перейти на полное сканирование таблицы banana (сложность = NxMx2 = NxM, где M = количество бананов).

Вы можете проверить это с помощью EXPLAIN SELECT ... каждого запроса.

Обратите внимание, что индекс с двумя столбцами на (banana.one, banana.two) будет таким же бесполезным.

+0

+1 для подробного объяснения – Stephan

1

Ваш оригинальный запрос

SELECT * 
FROM  apple 
LEFT JOIN banana b1 ON apple.id = b1.one 
LEFT JOIN banana b2 ON apple.id = b2.two 
WHERE b1.id IS NULL 
    AND b2.is IS NULL 

, кажется, ищет все в apples, что это не имеет соответствия banana на колонках banana.one и banana.two. Если это так, то почему вы не делаете очевидное и просто указав проблему ясно:

select * 
from apple a 
where not exists (select * 
        from banana b 
        where b.one = a.id 
       ) 
    and not exists (select * 
        from banana b 
        where b.two = a.id 
       ) 

Любой приличный оптимизатор запросов должен быть в состоянии сделать короткую работу, что, предполагая, что показатели по apple «s первичный ключ id и banana. Внешние ключевые столбцы one и two.

И если у вас был индекс покрытия на банановых колоннах one и two, т.е.

create index banana_one_two on banana (one , two) 

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

Рассмотрение плана выполнения, который вы получаете, скорее всего, даст вам полезную информацию о том, что случилось.

+0

+1 для оптимизации 'NOT EXISTS' – Stephan