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

157

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

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
  • 4
    Возможный дубликат соединения против подзапроса
  • 1
    Не дубликат Этот вопрос конкретно о разнице в производительности. Другой вопрос более общий, открытый о плюсах и минусах каждого подхода и почему один подход кажется более популярным.
Теги:
performance
join
database-design

14 ответов

138
Лучший ответ

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

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

  • 10
    +1 за использование объяснения
  • 2
    Обратите внимание, что DEPENDENT SUBQUERY означает то же самое, что и «коррелированный подзапрос».
36

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

  • 5
    Я не думаю, что это правда. Механизм SQL должен запустить подзапрос только один раз и использовать результат в виде списка.
  • 8
    Это зависит - если подзапрос каким-либо образом коррелирует с внешним запросом (использует его данные), он выполняется с каждой строкой.
Показать ещё 2 комментария
16

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

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

  • 0
    Отличная статья об улучшенном оптимизаторе MySQL 6.0, спасибо
7

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

6

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

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

4

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

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

Сделайте это:

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

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

4

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

4

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

3

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

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

  • 3
    Ха-ха, я <3 Sql убирает это голосование, потому что они не знают, как читать планы запросов.
  • 5
    Я понизил вас за использование слова "скрабы"
Показать ещё 1 комментарий
3

Подзапрос, вероятно, выполнял "полное сканирование таблицы". Другими словами, не использовать индекс и возвращать слишком много строк, из которых необходимо, чтобы отфильтровать "От главного запроса".

Просто догадывайтесь без подробностей, конечно, но об общей ситуации.

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

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

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

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

2

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

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

2

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

  • 1
    где действительно не плохо по своей сути.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню