Явные против неявных объединений SQL

399

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

SELECT * FROM
table a INNER JOIN table b
ON a.id = b.id;

против

SELECT a.*, b.*
FROM table a, table b
WHERE a.id = b.id;
dmanxiii
источник
11
Хороший вопрос. Мне любопытно, почему явное соединение используется вообще. Разве невозможно выполнить все запросы без него?
Андрей
6
используйте ключевое слово EXPLAIN, чтобы узнать разницу по обоим запросам .. используйте JOIN и посмотрите разницу .. Если вы попробуете в таблице более 100 тыс. записей, вы увидите разницу ...
Jeyanth Kumar
@andrew Мой вопрос был на самом деле, было ли неявное объединение формой «взлома» (как в «запросе, включающем более одной таблицы, без использования объединения? Это взлом, не так ли?»)
bobobobo
3
Они разные, неявное объединение будет удивлять вас время от времени при работе с нулевыми значениями; используйте явное соединение и избегайте ошибок, которые возникают, когда «ничего не изменилось!»
BlackTigerX
1
Нет никакой разницы. ,это CROSS JOINс более свободным и обязательным INNER JOINявляется CROSS JOINс ONкак , WHEREно крепче переплета. Для выполнения важно то, как СУБД оптимизирует запросы.
philipxy

Ответы:

132

По производительности они одинаковы (по крайней мере, в SQL Server).

PS: имейте в виду, что IMPLICIT OUTER JOINсинтаксис устарел начиная с SQL Server 2005. ( IMPLICIT INNER JOINСинтаксис, используемый в вопросе, все еще поддерживается)

Устаревание синтаксиса JOIN «старого стиля»: только частичная вещь

lomaxx
источник
4
@lomaxx, просто для ясности, не могли бы вы указать, какой синтаксис числа 2 в вопросе устарел?
J Wynia
8
Можете ли вы предоставить подтверждающую документацию? Это звучит неправильно на нескольких уровнях.
NotMe
21
Как вы отказываетесь от стандарта SQL?
Дэвид Кроушоу
7
@ david Crenshaw, неявное соединение больше не в стандарте и не было в течение 18 лет.
HLGEM
11
Так называемые «неявные объединения» «внутреннего» или «перекрестного» разнообразия остаются в Стандарте. SQL Server устарел синтаксис внешнего соединения "старого стиля" (то есть *=и =*), который никогда не был стандартным.
понедельник,
129

Лично я предпочитаю синтаксис объединения, так как он проясняет, что таблицы объединяются и как они объединяются. Попробуйте сравнить большие запросы SQL, где вы выбираете из 8 разных таблиц, и у вас есть много фильтрации в где. Используя синтаксис объединения, вы разделяете части, в которых объединяются таблицы, на части, где вы фильтруете строки.

Grom
источник
4
Я полностью согласен, но это не по теме. ОП спросил об эффективности.
villasv
56

В MySQL 5.1.51 оба запроса имеют идентичные планы выполнения:

mysql> explain select * from table1 a inner join table2 b on a.pid = b.pid;
+----+-------------+-------+------+---------------+------+---------+--------------+------+-------+
| id | select_type | table | type | possible_keys | key  | key_len | ref          | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+--------------+------+-------+
|  1 | SIMPLE      | b     | ALL  | PRIMARY       | NULL | NULL    | NULL         |  986 |       |
|  1 | SIMPLE      | a     | ref  | pid           | pid  | 4       | schema.b.pid |   70 |       |
+----+-------------+-------+------+---------------+------+---------+--------------+------+-------+
2 rows in set (0.02 sec)

mysql> explain select * from table1 a, table2 b where a.pid = b.pid;
+----+-------------+-------+------+---------------+------+---------+--------------+------+-------+
| id | select_type | table | type | possible_keys | key  | key_len | ref          | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+--------------+------+-------+
|  1 | SIMPLE      | b     | ALL  | PRIMARY       | NULL | NULL    | NULL         |  986 |       |
|  1 | SIMPLE      | a     | ref  | pid           | pid  | 4       | schema.b.pid |   70 |       |
+----+-------------+-------+------+---------------+------+---------+--------------+------+-------+
2 rows in set (0.00 sec)

table1имеет 166208 рядов; table2имеет около 1000 строк.

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

Мэтт Фенвик
источник
Это должен быть принятый ответ. Это правильно, план такой же (или близкий к более крупным утверждениям), но количество записей будет радикальным, что приведет к разнице в производительности.
Советская граница
37

Второй синтаксис имеет нежелательную возможность перекрестного соединения: вы можете добавлять таблицы в часть FROM без соответствующего предложения WHERE. Это считается вредным.

edosoft
источник
Что если имена таблиц в предложении from генерируются из таблиц, используемых в предложении where?
Jus12
Вы также можете выполнять перекрестное соединение с явным синтаксисом JOIN ( stackoverflow.com/a/44438026/929164 ). Возможно, вы имели в виду, что оно менее строгое, поэтому более подвержено ошибкам пользователя.
Даниэль Дубовски
15

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

Я согласен с Grom, что вы должны использовать синтаксис соединения ANSI. По их словам, главная причина в ясности. Вместо того, чтобы иметь предложение where с большим количеством предикатов, некоторые из которых объединяют таблицы, а другие ограничивают строки, возвращаемые с помощью синтаксиса объединения ANSI, вы даете ослепительно четкое представление о том, какие условия используются для объединения ваших таблиц, а какие - для ограничения Результаты.

andy47
источник
5

С точки зрения производительности они точно такие же (по крайней мере, в SQL Server), но имейте в виду, что они не поддерживают этот синтаксис объединения, и он не поддерживается sql server2005 из коробки.

Я думаю, что вы думаете об устаревших операторах * = и = * против "внешнего соединения".

Я только что проверил два приведенных формата, и они правильно работают в базе данных SQL Server 2008. В моем случае они дали одинаковые планы выполнения, но я не мог с уверенностью сказать, что это всегда будет правдой.

Joshdan
источник
5

@lomaxx: Просто чтобы прояснить, я почти уверен, что оба вышеуказанных синтаксиса поддерживаются SQL Serv 2005. Однако нижеприведенный синтаксис НЕ поддерживается

select a.*, b.*  
from table a, table b  
where a.id *= b.id;

В частности, внешнее соединение (* =) не поддерживается.

deadbug
источник
2
Честно говоря, я бы не использовал его даже в SQL Server 2000, синтаксис * = часто дает неправильные ответы. Иногда это интерпретирует как перекрестные соединения.
HLGEM
2

В некоторых базах данных (особенно в Oracle) порядок объединений может существенно повлиять на производительность запросов (если имеется более двух таблиц). В одном приложении мы имели буквально разность в два порядка в некоторых случаях. Использование синтаксиса внутреннего соединения дает вам контроль над этим - если вы используете правильный синтаксис подсказок.

Вы не указали, какую базу данных вы используете, но вероятность подсказывает, что SQL Server или MySQL там, где это не имеет никакого значения.

Ли Колдуэлл
источник
1
Ли, вы можете использовать подсказки и в неявных соединениях.
SquareCog
1
В Oracle крайне редко, когда порядок соединения оказывает существенное влияние на план выполнения. Смотрите эту статью Джонатана Льюиса для объяснения.
Джон Хеллер
1

Как заявил Ли Колдуэлл, оптимизатор запросов может создавать различные планы запросов на основе того, что функционально выглядит как один и тот же оператор SQL. Для дальнейшего чтения по этому вопросу, посмотрите на следующие два сообщения в блоге: -

Одна публикация от команды оптимизатора Oracle

Еще одна публикация из блога "Структурированные данные"

Я надеюсь, что вы найдете это интересным.

Майк Макаллистер
источник
Майк, разница, о которой они говорят, заключается в том, что вы должны быть уверены, что если вы укажете явное соединение, вы зададите условие объединения, а не фильтр. Вы заметите, что для семантически правильных запросов план exec такой же.
SquareCog
1

Производительность мудрая, это не должно иметь никакого значения. Явный синтаксис объединения кажется мне чище, поскольку он четко определяет отношения между таблицами в предложении from и не загромождает предложение where.

Дэвид
источник
0

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

При работе с внутренними объединениями нет никакой реальной разницы в удобочитаемости, однако это может усложниться при работе с левым и правым объединениями, поскольку в более старом методе вы получите что-то вроде этого:

SELECT * 
FROM table a, table b
WHERE a.id = b.id (+);

Выше приведен старый способ написания левого соединения, в отличие от следующего:

SELECT * 
FROM table a 
LEFT JOIN table b ON a.id = b.id;

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

Возвращаясь к основной теме, для компилятора SQL не имеет значения, как написан запрос, поскольку он обрабатывает их одинаково. Я видел смесь обоих в базах данных Oracle, в которые записывалось много людей, как старших, так и младших. Опять же, все сводится к тому, насколько читаем сценарий и команда, с которой вы работаете.

Мишель Ла Ферла
источник
-1

По моему опыту, использование синтаксиса кросс-объединения с условием где-то часто приводит к повреждению мозга, особенно если вы используете продукт Microsoft SQL. Например, способ, которым SQL Server пытается оценить количество строк таблицы, дико ужасен. Использование синтаксиса внутреннего соединения дает вам некоторый контроль над тем, как выполняется запрос. Таким образом, с практической точки зрения, учитывая атавистическую природу современных технологий баз данных, вы должны идти с внутренним объединением.

Шон
источник
5
У вас есть доказательства этого? Потому что принятый ответ говорит иначе.
cimmanon