Подзапросы против объединений

158

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

WHERE id IN (SELECT id FROM ...)

Реорганизованный запрос выполняется примерно в 100 раз быстрее. (~ 50 секунд до ~ 0,3) Я ожидал улучшения, но кто-нибудь может объяснить, почему это так радикально? Все столбцы, использованные в предложении where, были проиндексированы. SQL выполняет запрос в предложении where один раз для строки или чего-то еще?

Обновление - Объясните результаты:

Разница заключается во второй части запроса "where id in ()" -

2   DEPENDENT SUBQUERY  submission_tags ref st_tag_id   st_tag_id   4   const   2966    Using where

vs 1 проиндексированная строка с объединением:

    SIMPLE  s   eq_ref  PRIMARY PRIMARY 4   newsladder_production.st.submission_id  1   Using index
palmsey
источник
4
Возможный дубликат соединения и подзапроса
Сиро Сантилли 法轮功 冠状 病 六四 事件 法轮功
2
Не дубликат Этот вопрос конкретно о разнице в производительности. Другой вопрос более общий, открытый о плюсах и минусах каждого подхода и почему один подход кажется более популярным.
Василий Бурк
@simhumileco Это не улучшение, это не разница, это противоречит тому, что написал автор, и такого рода редактирование стиля кода неуместно. Когда я должен внести изменения в код?
Philipxy
Привет @philipxy, я не собирался вмешиваться в мысли автора, но только чтобы сделать фрагмент кода более читабельным и написанным более тщательно.
simhumileco

Ответы:

160

«Коррелированный подзапрос» (т. Е. Тот, в котором условие where зависит от значений, полученных из строк содержащего запроса) будет выполняться один раз для каждой строки. Некоррелированный подзапрос (в котором условие where не зависит от содержащего запроса) будет выполнен один раз в начале. Механизм SQL делает это различие автоматически.

Но, да, план объяснения даст вам грязные детали.

Джеффри Л Уитледж
источник
3
Обратите внимание, что это DEPENDENT SUBQUERYозначает то же самое, что и «коррелированный подзапрос».
Тимо
38

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

Sklivvz
источник
5
Я не думаю, что это правда. Механизм SQL должен выполнить подзапрос только один раз и использовать результат в виде списка.
Дакракот
8
Это зависит - если подзапрос каким-либо образом коррелирует с внешним запросом (использует его данные), он выполняется с каждой строкой.
qbeuek
4
Это, вероятно, верно в этом случае, но это не так в целом.
Эми Б
1
ОП EXPLAINговорит DEPENDENT SUBQUERY, что является наиболее ярким показателем такого поведения.
Тимо
16

Вот пример того, как подзапросы оцениваются в MySQL 6.0 .

Новый оптимизатор преобразует этот вид подзапросов в объединения.

Джузеппе Максия
источник
Отличная статья об улучшенном оптимизаторе MySQL 6.0, спасибо
Fire Crow
7

Запустите объяснительный план для каждой версии, он скажет вам, почему.

Scotta
источник
6

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

Без просмотра запроса трудно сказать, что было плохого в оригинале, но я думаю, что это было то, что оптимизатор просто не мог сделать намного лучше. Запуск объяснения покажет вам метод оптимизаторов для получения данных.

pfranza
источник
4

Посмотрите на план запроса для каждого запроса.

Где in и Join, как правило, могут быть реализованы с использованием одного и того же плана выполнения, поэтому, как правило, скорость переключения между ними равна нулю.

Эми Б
источник
3
Ха-ха, я <3 Sql убирает это голосование, потому что они не знают, как читать планы запросов.
Эми Б
4

Оптимизатор не очень хорошо поработал. Обычно они могут быть преобразованы без какой-либо разницы, и оптимизатор может сделать это.

Кейд Ру
источник
4

Обычно это результат того, что оптимизатор не может выяснить, что подзапрос может быть выполнен как объединение, и в этом случае он выполняет подзапрос для каждой записи в таблице, а не соединяет таблицу в подзапросе с запрашиваемой таблицей. Некоторые из более «предприимчивых» баз данных лучше справляются с этим, но они все еще иногда упускают это.

Марк Родди
источник
4

Этот вопрос несколько общий, поэтому вот общий ответ:

По сути, запросы занимают больше времени, когда в MySQL есть тонны строк для сортировки.

Сделай это:

Запустите EXPLAIN для каждого из запросов (один из них - JOIN, а затем - для Subqueried) и опубликуйте результаты здесь.

Я думаю, что увидеть разницу в интерпретации этих запросов MySQL будет полезным для всех.

Пит Карл II
источник
4

Подзапрос where должен выполнить 1 запрос для каждой возвращаемой строки. Внутреннее объединение просто должно выполнить 1 запрос.

Шон
источник
3

Подзапрос, вероятно, выполнял «полное сканирование таблицы». Другими словами, не используя индекс и возвращая слишком много строк, которые нужно было отфильтровать из основного запроса Where.

Просто предположение без подробностей, конечно, но это обычная ситуация.

igelkott
источник
2

С подзапросом вы должны повторно выполнить 2-й SELECT для каждого результата, и каждое выполнение обычно возвращает 1 строку.

С объединением, 2-й SELECT возвращает намного больше строк, но вы должны выполнить его только один раз. Преимущество состоит в том, что теперь вы можете присоединиться к результатам, а объединение отношений - это то, что база данных должна быть хороша. Например, возможно, оптимизатор может определить, как лучше использовать индекс сейчас.

Джоэл Коухорн
источник
2

Это не столько подзапрос, сколько предложение IN, хотя объединения лежат в основе по крайней мере механизма SQL Oracle и работают очень быстро.

dacracot
источник
1
где действительно не плохо по своей сути.
Шон
2

Взято из Справочного руководства ( 14.2.10.11 Перезапись подзапросов как объединений ):

LEFT [OUTER] JOIN может быть быстрее, чем эквивалентный подзапрос, потому что сервер может быть в состоянии оптимизировать его лучше - факт, который не относится только к MySQL Server.

Таким образом, подзапросы могут быть медленнее, чем LEFT [OUTER] JOINS.

simhumileco
источник