Используя ключевое слово JOIN или нет


45

Следующие запросы SQL одинаковы:

SELECT column1, column2
FROM table1, table2
WHERE table1.id = table2.id;

SELECT column1, column2
FROM table1 JOIN table2 
ON table1.id = table2.id;

И, конечно же, приводят к одинаковым планам запросов на каждой СУБД, которую я когда-либо пробовал.

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

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

Является ли одна из этих форм объективно лучше другой? Если нет, то каковы причины использования одного над другим?


1
Почему бы не описать это и дать остальным знать результат? Вообще говоря, производительность очень сильно перевешивает предпочтения стиля.
Демиан Брехт

3
«привести к одинаковым планам запросов на каждой СУБД, которую я когда-либо пробовал». Если бы у этого мог быть ответ с точки зрения производительности, он бы спросил об этом на stackoverflow.com. увы, это один и тот же запрос.
SingleNegationElimination

Ах .. Пропустил это :)
Демиан Брехт

2
«Субъективный» не означает «каково ваше мнение». Я отредактировал это, чтобы отчасти соответствовать критериям, изложенным в FAQ .
Аарона

Я также склоняюсь к этому стилю, чтобы минимизировать удивление. Думаю, вы только что ответили на свой вопрос. Сюрпризы плохие.
Питер Б

Ответы:


60

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

Например, возьмите следующий запрос:

select *
from table1, table2, table3, table4
where table1.id = table2.id
and table2.id = table3.id
and table3.id = table4.id
and table1.column1 = 'Value 1'

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

Однако теперь возьмите этот код:

select *
from table1 join table2 on table1.id = table2.id
join table3 on table2.id = table3.id
join table4 on table3.id = table4.id
where table1.column1 = 'Value 1'

В этом случае все, что связано с таблицами или их связями, изолировано от предложения from, тогда как фактическая бизнес-логика для ограничения запросов находится в предложении where. Я думаю, что это гораздо более понятно, особенно для больших запросов.


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

5
+1 Для «разделения проблем» объединения объединяют данные, где предложения определяют подмножества данных, которые вас интересуют.

39

Синтаксис объединения заменил старый синтаксис запятой в 1992 году. В настоящее время нет причин писать код с синтаксисом запятой. Вы ничего не получаете и у вас есть проблемы, которых у вас просто нет с явным синтаксисом.

Во-первых, когда вы получаете более сложные запросы, очень легко выполнить случайное перекрестное соединение, пропустив условие where. Это то, что может предотвратить явный синтаксис соединения, поскольку вы получите синтаксическую ошибку.

Если вы намереваетесь использовать перекрестное объединение, явный синтаксис соединения прояснит это, в то время как в неявном синтаксисе кто-то, выполняющий обслуживание, может предположить, что вы забыли добавить предложение where.

Затем возникает проблема левого и правого объединений, которые являются проблематичными по крайней мере в некоторых БД с использованием неявного синтаксиса. Они устарели в SQL Server и фактически не дают достоверных результатов даже в старых версиях. Ни один запрос, требующий внешнего объединения, не должен содержать неявный синтаксис в SQL Server.

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

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


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

8

Ха. Я просто нашел возможный ответ на свой вопрос, просматривая документацию по PostgreSQL . Суммируя, что объясняется на этой странице, результирующий запрос остается тем же, но число планов, которые оптимизатор должен учитывать, растет экспоненциально с количеством соединений.

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

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

Следует отметить, что поведение по умолчанию одинаково в обоих случаях, и для получения альтернативных планов требуется знание внутренних структур dbms и особенностей рассматриваемых таблиц, чтобы получить другой результат


2
Вы немного неправильно поняли эти документы, как бы то ни было. Во-первых, на самом деле есть три порога. Один запускает GEQO, как вы указали; другие два (из-за пределов коллапса и слияния) заканчивают тем, что заставляют строгать выбирать нужные индексы, а не реорганизовывать порядок соединения. Во-вторых, что не менее важно, запросы переписываются так же, как они анализируются. Это приводит к тому, что первый из запросов примера разбирается в том же дереве запросов, что и дерево второго - пороги затем сообщают PG, должна ли она попытаться изменить порядок соединений или нет.
Дени де Бернарди

8

Хорошо, вот представление теории множеств:

Когда вы используете запятую для разделения двух (или более) имен таблиц, вы имеете в виду декартово произведение. Каждая строка «левой» таблицы будет «сопоставлена» (сцеплена) со строкой правой таблицы.

Теперь, если вы что-то напишите в предложении where, это все равно что поставить условие для этой «конкатенации», сообщающее, какие строки «конкатенировать» и с какими строками.

На самом деле это «объединение» строк :) и, следовательно, ключевое слово join, которое помогает обеспечить более читаемый синтаксис и более понятно, что вы «действительно» хотите объединить некоторые общие значения. Похоже на то, что @Dustin разъяснил выше.

Теперь каждая СУБД умна, т. Е. Она не рассчитывает сначала декартово произведение, а затем отфильтровывает данные (крайне расточительно), а скорее делает это на основе структуры запроса. Единственное, о чем я могу подумать, это то, что когда вы просите его «присоединиться», это все равно, что сделать присоединение явным и, вероятно, помогает выполнить код быстрее (насколько? Вам придется профилировать его и видеть), но в Разделенный запятыми регистр требует некоторого времени, чтобы «определить» оптимальную стратегию. Я могу ошибаться, но я просто делаю обоснованное предположение о том, как можно было бы его кодировать ...


5

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

Если в будущем возникнет ситуация, которая потребует изменения оператора с INNER JOIN на OUTER JOIN, это будет гораздо проще сделать со вторым оператором.


3

Любая СУБД сделает их одинаковыми с точки зрения исполнения. Все сводится к тому, является ли человек более читабельным и выразительным.

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

select name, deptname
from people p, departments d
where p.deptid = d.id and p.is_temp = 'Y'

против

select name, deptname
from people p
    inner join departments d on p.deptid = d.id
where p.is_temp = 'Y'

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


1

Я только однажды видел, как эти два результата приводят к разному набору оптимизаций, и если память обслуживает, это было в ms-sql2k по очень сложному запросу. В этом примере старая форма, использованная с * =, в 4 раза увеличила производительность. Никто, включая наших технических ребят из Microsoft, не мог объяснить почему. Ребята из MS назвали это ошибкой. Я никогда не видел это снова.

Поскольку большинство СУБД достаточно умны, чтобы не выполнять полную картезианскую функцию, главная причина, по которой я могу не использовать ее (кроме того, что она устарела), заключается в том, что большинство людей в возрасте до 30-35 лет, с которыми я работал, никогда не видели старые формы раньше и теряются ужасно, когда они сталкиваются с ним.


Конечно, этот синтаксис левого соединения никогда не давал правильных результатов (см. BOL для SQL Server 2000), поэтому даже если бы он был быстрее, я бы его заменил.
HLGEM

Я никогда не сталкивался с этим, и поиск со звездочкой никогда не заканчивается хорошо, у вас есть пример?
Билл

-1

Старый стиль устарел, вы не должны его использовать.

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


Я думаю, что этот ответ на самом деле ничего не добавляет, не говоря, почему он устарел и не должен использоваться.
RemcoGerlich

1
@RemcoGerlich, почему это устарело, здесь не обсуждается. Здесь обсуждается, использовать ли старый или новый синтаксис. Является ли одно лучше, чем другое, или нет, это спорный вопрос: вы не должны использовать старый синтаксис. Вопрос почему это еще одно обсуждение. (тот, который был урегулирован 20 лет назад.)
Питер Б.

-4

Одна из причин более лаконичного синтаксиса заключается в том, что он более лаконичен, поэтому, если вам удобно, его легче читать. Я думаю, что подробный случай похож на написание арифметики на языке COBOL, например, MULTIPLY A BY B CIVING C.


Downvoters: Есть ли что-то фактически неправильное в этом ответе, или они были просто "несогласны с вами" downvotes?
Адам Либуша
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.