Есть ли разница в выполнении между условием JOIN и условием WHERE?

17

Есть ли разница в производительности между этими двумя примерами запросов?

Запрос 1:

select count(*)
from   table1 a
join   table2 b
on     b.key_col=a.key_col
where  b.tag = 'Y'

Запрос 2;

select count(*)
from   table1 a
join   table2 b
on     b.key_col=a.key_col
   and b.tag = 'Y'

Обратите внимание, единственное отличие заключается в размещении дополнительного условия; первый использует WHEREпредложение, а второй добавляет условие к ONпредложению.

Когда я выполняю эти запросы в моей системе Teradata, планы объяснения идентичны, и шаг СОЕДИНЕНИЕ показывает дополнительное условие в каждом случае. Тем не менее, на этот вопрос SO, касающийся MySQL, один из ответов предположил, что второй стиль предпочтительнее, потому что WHEREобработка происходит после создания соединений.

Есть ли общее правило, которому нужно следовать при кодировании таких запросов? Я предполагаю, что это должно зависеть от платформы, поскольку это, очевидно, не имеет значения для моей базы данных, но, возможно, это просто особенность Teradata. И если это платформа зависит, я бы очень хотел , чтобы получить несколько ссылок документации; Я действительно не знаю, что искать.

BellevueBob
источник
9
Это зависит от платформы, поскольку зависит от того, как оптимизатор СУБД справляется с анализом и оптимизацией.
Philᵀᴹ
8
И этот ответ в связанном вопросе заслуживает нескольких отрицательных ответов. Даже примитивный оптимизатор MySQL понимает, что эти простые запросы эквивалентны и что «предложение WHERE оценивается после того, как все соединения были сделаны» , верно только на логическом уровне, а не в реальном выполнении.
ypercubeᵀᴹ
1
Не совсем дубликат; этот вопрос и ответы сравнивали «неявный» и «явный» синтаксис JOIN. Я спрашиваю конкретно о дополнительных условиях присоединения.
BellevueBob
Не собираюсь отправлять пост в ответ, как я пробовал раньше и получил много отрицательных голосов. Когда есть много объединений, у меня есть опыт случаев, когда условие включается в объединение, что приводит к лучшему плану запросов (он фильтруется раньше). Все те же результаты.
Папараццо

Ответы:

14

Согласно главе 9 («Анализатор и оптимизатор»), страница 172 книги Саша Пачева «Понимание внутренних функций MySQL»

Понимание внутренних особенностей MySQL

Вот разбивка оценки запроса на следующие задачи:

  • Определите, какие ключи можно использовать для извлечения записей из таблиц, и выберите лучший для каждой таблицы.
  • Для каждой таблицы определите, лучше ли сканирование таблицы, чем чтение по ключу. Если имеется много записей, соответствующих значению ключа, преимущества ключа уменьшаются, и сканирование таблицы становится быстрее.
  • Определите порядок объединения таблиц, когда в запросе присутствует более одной таблицы.
  • Перепишите предложения WHERE, чтобы устранить мертвый код, сократив ненужные вычисления и по возможности изменив ограничения, чтобы открыть путь для использования ключей.
  • Исключите неиспользуемые таблицы из объединения.
  • Определите, можно ли использовать ключи для ORDER BYи GROUP BY.
  • Попытайтесь упростить подзапросы, а также определить, в какой степени их результаты могут быть кэшированы.
  • Объединить представления (разверните ссылку на представление как макрос)

На той же странице написано следующее:

В терминологии оптимизатора MySQL каждый запрос представляет собой набор соединений. Термин « соединение» используется здесь более широко, чем в командах SQL. Запрос только к одной таблице является вырожденным соединением. Хотя мы обычно не рассматриваем чтение записей из одной таблицы как объединение, те же структуры и алгоритмы, которые используются с обычными объединениями, прекрасно работают для разрешения запроса только с одной таблицей.

Эпилог

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

Я писал об этой причудливости раньше

потому что MySQL Query Optimizer может заставить отклонить определенные ключи во время оценки запроса.

@ Комментарий Фила поможет мне увидеть, как опубликовать этот ответ (+1 за комментарий Фила)

Комментарий @ ypercube (+1 к этому тоже) - это компактная версия моего поста, потому что Оптимизатор запросов MySQL примитивен. К сожалению, так должно быть, поскольку он имеет дело с внешними механизмами хранения.

ВЫВОД

Что касается вашего фактического вопроса, MySQL Query Optimizer будет определять показатели производительности каждого запроса, когда он будет выполнен

  • подсчет строк
  • выбирая ключи
  • массируя прерывистые наборы результатов
  • О, да, делая фактическое присоединение

Вам, вероятно, придется навести порядок выполнения, переписав (рефакторинг) запрос

Вот первый запрос, который вы дали

select count(*)
from   table1 a
join   table2 b
on     b.key_col=a.key_col
where  b.tag = 'Y';

Попробуйте переписать его, чтобы сначала оценить ГДЕ

select count(*)
from   table1 a
join   (select key_col from table2 where tag='Y') b
on     b.key_col=a.key_col;

Это определенно изменит план EXPLAIN. Это может привести к лучшим или худшим результатам.

Однажды я ответил на вопрос в StackOverflow, где я применил эту технику. Объяснение было ужасным, но представление было динамитом. Это работало только из-за наличия правильных индексов и использования LIMIT в подзапросе .

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

RolandoMySQLDBA
источник
2
+1 за подробную информацию, специфичную для MySQL, и особенно за то, что меня обманули, чтобы узнать разницу между «Эпилогом» и «Заключением»!
BellevueBob
В моем посте Эпилог - это подведение итогов.
RolandoMySQLDBA
6
@Rolando: Вы можете добавить Последствия об улучшениях оптимизаторов в последних версиях MariaDB (5.3 и 5.5) и в недавно выпущенной основной версии MySQL (5.6). Что может сделать переписывание ненужным.
ypercubeᵀᴹ
1

Для Oracle, поскольку у MySQL было длинное описание, у нас есть 2 высокоуровневых способа использования оптимизатора.

Во-первых, это оптимизация на основе правил (или RBO). У Oracle есть 15 установленных правил, которые каждый синтаксический запрос пытается выполнить в установленном порядке. Если он не может сгенерировать оптимизированный запрос из правила 1, он переходит к правилу 2 и далее, пока не достигнет правила 15.

для получения дополнительной информации: https://docs.oracle.com/cd/B10500_01/server.920/a96533/rbo.htm

Они влияют на ядра СУБД Oracle версии 11.1 и ниже, которые не были преобразованы в оптимизатор на основе затрат (он же CBO). Oracle 11.2 и выше требуют оптимизатора CBO, но могут принудительно оптимизировать определенные идентификаторы Sql в старом методе RBO, если пользователь хочет.

Вместо этого CBO для Oracle 11.1+ составляет несколько планов выполнения для одного и того же идентификатора SQL и выполняет план с наименьшей общей ожидаемой стоимостью. Он использует большую часть логики RBO, но анализирует статистику таблиц, чтобы создать динамический план затрат на выполнение для каждой операции, которую БД должна выполнить, чтобы предоставить конечному пользователю свои данные. Выполнение полного сканирования таблицы на очень больших таблицах действительно дорого; Выполнение полного сканирования таблицы на таблице с 10 строками - это дешево. В РБО это считалось равными операциями.

для получения дополнительной информации: https://oracle-base.com/articles/misc/cost-based-optimizer-and-database-statistics

Для вашего конкретного примера запроса: Oracle, вероятно, проанализирует информацию, чтобы составить другие планы выполнения, и, таким образом, один будет технически лучше другого. Однако это может быть минимальной разницей. Глядя на это, Oracle RBO и CBO хотели бы запрос 1 больше, потому что он выполняет соединение при меньших условиях, а затем отфильтровывает определенный столбец из временной таблицы, которую он создал из соединения.

JB-Learner
источник
1

Если у вас есть два запроса, и вы думаете, что они эквивалентны, то может произойти следующее:

  1. Оба запроса имеют одинаковый план выполнения. Это хорошо, и это то, что мы ожидаем. Будем надеяться, что это оптимальный план выполнения запроса.
  2. Есть разные планы выполнения. У нас есть два случая здесь.

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

    2.2 Запросы имеют разные планы выполнения, и один план лучше другого. Опять у нас есть подсистемы:

    2.2.1 Планы отличаются, потому что запросы не эквивалентны. Поэтому внимательно проверьте, действительно ли они эквивалентны. В вашем случае они действительно эквивалентны.

    2.2.2 Планы разные, но запросы эквивалентны. Это означает, что оптимизатор не достаточно зрелый. В идеальном мире с идеальными оптимизаторами этого не должно быть. Так что да, это зависит от платформы, и вы должны изучить конкретные документы платформы, чтобы выяснить, почему это происходит.

    2.2.3 Планы разные, запросы эквивалентны, в программном обеспечении базы данных есть ошибка.

miracle173
источник