Я реорганизовал медленный раздел приложения, унаследованного от другой компании, чтобы использовать внутреннее соединение вместо подзапроса, например
where id in (select id from ... )
Рефакторизованный запрос выполняется примерно в 100 раз быстрее. (от 50 секунд до ~ 0,3) Я ожидал улучшения, но может ли кто-нибудь объяснить, почему он был настолько резким? Колонки, используемые в предложении where, были проиндексированы. Выполняет ли SQL запрос в предложении where один раз в строке или что-то еще?
Обновить. Объясните результаты:
Разница во второй части "где id in()" query -
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
A "коррелированный подзапрос" (то есть тот, в котором условие where зависит от значений, полученных из строк содержащего запроса) будет выполняться один раз для каждой строки. Некорректированный подзапрос (тот, в котором условие where не зависит от содержащего запроса) будет выполняться один раз в начале. Механизм SQL делает это различие автоматически.
Но, да, объяснительный план даст вам грязные детали.
DEPENDENT SUBQUERY
означает то же самое, что и «коррелированный подзапрос».
Вы выполняете подзапрос один раз для каждой строки, тогда как соединение происходит по индексам.
Здесь пример того, как подзапросы оцениваются в MySQL 6.0.
Новый оптимизатор преобразует такие подзапросы в объединения.
Запустите план объяснения для каждой версии, он скажет вам, почему.
до того, как запросы будут выполняться против набора данных, которые они помещают через оптимизатор запросов, оптимизатор пытается организовать запрос таким образом, что он может удалить столько наборов (строк) из набора результатов, насколько это возможно. Часто, когда вы используете подзапросы (особенно плохие), кортежи нельзя вырезать из набора результатов до тех пор, пока внешний запрос не начнет работать.
Не видя запроса, трудно сказать, что было так плохо в оригинале, но я предполагаю, что это было то, что оптимизатор просто не мог сделать намного лучше. Запуск "объяснения" покажет вам метод оптимизаторов для извлечения данных.
Этот вопрос несколько общий, поэтому здесь общий ответ:
В основном, запросы занимают больше времени, когда MySQL имеет множество строк для сортировки.
Сделайте это:
Запустите EXPLAIN для каждого из запросов (JOIN'ed, затем Subqueried) и опубликуйте результаты здесь.
Я думаю, что различие в интерпретации MySQL этих запросов было бы опытом обучения для всех.
В подзапросе должен быть запущен 1 запрос для каждой возвращаемой строки. Внутреннее соединение просто должно выполнить 1 запрос.
Обычно это результат оптимизации, который не может понять, что подзапрос может выполняться как объединение, и в этом случае он выполняет подзапрос для каждой записи в таблице, а затем присоединяет таблицу в подзапросе к таблице, которую вы запрашивают. Некоторые из более "хозяйственных" баз данных лучше в этом, но они по-прежнему упускают его иногда.
Посмотрите план запроса для каждого запроса.
В тех случаях, когда in и Join могут обычно реализовываться с использованием одного и того же плана выполнения, поэтому обычно происходит нулевое ускорение от изменения между ними.
Подзапрос, вероятно, выполнял "полное сканирование таблицы". Другими словами, не использовать индекс и возвращать слишком много строк, из которых необходимо, чтобы отфильтровать "От главного запроса".
Просто догадывайтесь без подробностей, конечно, но об общей ситуации.
Взято из справочного руководства (14.2.10.11 Перезаписывание подзапросов как соединений):
LEFT [OUTER] JOIN может быть быстрее, чем эквивалентный подзапрос, потому что сервер может оптимизировать его лучше - факт, который не специфичен только для MySQL Server.
Таким образом, подзапросы могут быть медленнее, чем LEFT [OUTER] JOINS.
При подзапросе вы должны повторно выполнить второй SELECT для каждого результата, и каждое выполнение обычно возвращает 1 строку.
При соединении второй SELECT возвращает намного больше строк, но вы должны выполнить его только один раз. Преимущество состоит в том, что теперь вы можете присоединиться к результатам, и объединение отношений - это то, что должна делать база данных. Например, возможно, оптимизатор может определить, как лучше воспользоваться индексом сейчас.
Это не столько подзапрос, сколько предложение IN, хотя объединения основаны, по крайней мере, на Oracle SQL engine и работают очень быстро.