ВНУТРЕННЕЕ ПРИСОЕДИНЕНИЕ НА ПУТЬ К ГДЕ

941

Для простоты предположим, что все соответствующие поля NOT NULL.

Ты можешь сделать:

SELECT
    table1.this, table2.that, table2.somethingelse
FROM
    table1, table2
WHERE
    table1.foreignkey = table2.primarykey
    AND (some other conditions)

Или еще:

SELECT
    table1.this, table2.that, table2.somethingelse
FROM
    table1 INNER JOIN table2
    ON table1.foreignkey = table2.primarykey
WHERE
    (some other conditions)

Эти два работают одинаково MySQL?

JCCyC
источник
1
@Marco: вот он
Александр Малахов
1
возможный дубликат SQL левого соединения против нескольких таблиц в строке FROM?
Сиро Сантилли 郝海东 郝海东 病 六四 事件 法轮功
18
Если я правильно понял, первый вариант - это неявный синтаксис ANSI SQL-89, а второй - это явный синтаксис объединения ANSI SQL-92. Оба приведут к одному и тому же результату в соответствующих реализациях SQL, и оба приведут к одному и тому же плану запросов в хорошо выполненных реализациях SQL. Я лично предпочитаю синтаксис SQL-89, но многие люди предпочитают синтаксис SQL-92.
Микко Ранталайнен
11
@ Хоган Я указывал официальные названия для разных синтаксисов. Ни в одном из ответов не были указаны полные имена, поэтому я решил добавить их в качестве комментариев. Однако мой комментарий не ответил на реальный вопрос, поэтому я добавил его как комментарий, а не как ответ. (Ответы с высоким рейтингом имеют такие утверждения, как «ВНУТРЕННЕЕ СОЕДИНЕНИЕ - это синтаксис ANSI» и «Синтаксис неявного соединения ANSI старше», который вообще ничего не говорит, потому что оба синтаксиса - это разные синтаксисы ANSI.)
Микко Ранталайнен

Ответы:

710

INNER JOIN это синтаксис ANSI, который вы должны использовать.

Как правило, он считается более читабельным, особенно когда вы объединяете множество таблиц.

Он также может быть легко заменен OUTER JOIN всякий раз, когда возникает необходимость.

WHEREСинтаксис ориентирован более реляционная модель.

Результат двух таблиц JOIN ed является декартово произведение таблиц, к которым применен фильтр, который выбирает только те строки с сопоставлением соединяющих столбцов.

Это легче увидеть с WHERE синтаксиса.

Что касается вашего примера, в MySQL (и в SQL в целом) эти два запроса являются синонимами.

Также обратите внимание, что MySQL также имеет STRAIGHT_JOIN пункт.

Используя этот пункт, вы можете контролировать JOIN порядком: какая таблица сканируется во внешнем цикле, а какая во внутреннем цикле.

Вы не можете контролировать это в MySQL, используя WHEREсинтаксис.

Quassnoi
источник
10
Спасибо, Кассной. У вас есть много деталей в ваших ответах; Справедливо ли говорить, что «да, эти запросы эквивалентны, но вы должны использовать внутреннее соединение, потому что оно более читабельно и его легче модифицировать»?
allyourcode
8
@allyourcode: для Oracle, SQL Server, MySQLи PostgreSQL- да. Для других систем, наверное, тоже, но вам лучше проверить.
Кассной
13
FWIW, использование запятых с условиями соединения в WHEREразделе также в стандарте ANSI.
Билл Карвин
1
@Bill Karwin: JOINКлючевое слово не было частью проприетарных стандартов до недавнего времени, что может показаться. Он вошел Oracleтолько в версии 9и PostgreSQLв версии 7.2(обе выпущены в 2001). Внешний вид этого ключевого слова был частью ANSIстандартного принятия, и именно поэтому это ключевое слово обычно ассоциируется с ним ANSI, несмотря на то, что последнее поддерживает также запятую как синоним для CROSS JOIN.
Quassnoi
9
Тем не менее, указанные в ANSI SQL-89 объединения должны выполняться с запятыми и условиями в WHEREпредложении (как вы сказали, соединение без условий эквивалентно перекрестному соединению). ANSI SQL-92 добавил JOINключевое слово и связанный с ним синтаксис, но синтаксис в стиле запятых все еще поддерживается для обеспечения обратной совместимости.
Билл Карвин
182

Другие отметили, что это INNER JOINпомогает читабельности человека, и это, я согласен, главный приоритет.
Позвольте мне попытаться объяснить, почему синтаксис объединения более читабелен.

Основной SELECTзапрос таков:

SELECT stuff
FROM tables
WHERE conditions

Предложение SELECTговорит нам, что мы получаем обратно; FROMпункт говорит нам , где мы получаем его от, и WHEREпункт говорит нам , которые из них мы получаем.

JOIN это утверждение о таблицах, как они связаны друг с другом (концептуально, фактически, в одну таблицу).

Любые элементы запроса, которые управляют таблицами - откуда мы берем материал - семантически принадлежат FROMпредложению (и, конечно, именно туда JOINидут элементы). Ввод Соединительные элементы в WHEREпункте смешивает , который и где-с , именно поэтому JOINпредпочтительным является синтаксис.

Карл Манастер
источник
7
Спасибо за разъяснение, почему внутреннее соединение предпочтительнее Карла. Я думаю, что ваш ответ был неявным в других, но явный обычно лучше (да, я фанат Python).
allyourcode
2
Семантика ON и WHERE означает, что для JOINs после последнего OUTER JOIN не имеет значения, какой вы используете. Хотя вы характеризуете ON как часть JOIN, это также фильтрация по декартовому произведению. И ON, и WHERE фильтруют декартово произведение. Но перед последним ВНЕШНИМ СОЕДИНЕНИЕМ необходимо использовать либо ВКЛ, либо подвыбор с ГДЕ . (JOIN не являются парами столбцов "on". Любые две таблицы могут быть объединены при любом условии. Это просто способ интерпретировать JOINs по равенству столбцов конкретно.)
philipxy
Даже когда вы используете WHERE для того же эффекта INNER JOIN, вы будете упоминать две таблицы в части FROM запроса. Таким образом, в основном вы по-прежнему подразумеваете, откуда вы берете свои данные в предложении FROM, так что, я думаю, вы не можете сказать, что это обязательно «объединяет, куда и откуда»
cybergeek654
@ArsenKhachaturyan То, что ключевое слово или идентификатор используется в тексте, не означает, что это код, и ему нужен формат кода. Это выбор форматирования, который может пойти любым путем, и если есть смысл отредактировать его здесь, то оправдано, чтобы каждое сообщение постоянно редактировалось в другом формате - то есть это не оправдано. (Плюс встроенный формат кода для каждого слова может быть трудным для чтения.) То же самое для разрывов абзаца здесь - они не особо проясняются. То же самое с "который" против "того". И названия языков программирования не должны быть в формате кода. PS Вы добавили разрыв строки по ошибке.
philipxy
@philipxy, как вы упомянули «это не значит ...», но, очевидно, это не означало, что его нельзя пометить ключевым словом кода. Да, это выбор, который нужно сделать, но многие посты создаются без знания этого факта. Следовательно, мое решение внести изменения не имеет целью что-либо сломать, а сделать его более читабельным. Если вы заметили какой-либо разрыв после форматирования изменений, извините за это, и вы, очевидно, можете отменить такие изменения.
Арсен Хачатурян
143

Применение условных операторов в ON / WHERE

Здесь я объяснил о шагах обработки логического запроса.


Ссылка: Внутри Microsoft® SQL Server ™ 2005 T-SQL Querying
Издатель: Microsoft Press
Pub Дата: 7 марта 2006 г.
Печать ISBN-10: 0-7356-2313-9
Печать ISBN-13: 978-0-7356-2313-2
Страницы: 640

Внутри Microsoft® SQL Server ™ 2005 T-SQL-запросов

(8)  SELECT (9) DISTINCT (11) TOP <top_specification> <select_list>
(1)  FROM <left_table>
(3)       <join_type> JOIN <right_table>
(2)       ON <join_condition>
(4)  WHERE <where_condition>
(5)  GROUP BY <group_by_list>
(6)  WITH {CUBE | ROLLUP}
(7)  HAVING <having_condition>
(10) ORDER BY <order_by_list>

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

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

Краткое описание этапов обработки логических запросов

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

  1. FROM: декартово произведение (перекрестное соединение) выполняется между первыми двумя таблицами в предложении FROM, и в результате создается виртуальная таблица VT1.

  2. ON: фильтр ON применяется к VT1. Только строки, для которых <join_condition>TRUE, вставляются в VT2.

  3. OUTER (соединение): если указано OUTER JOIN (в отличие от CROSS JOIN или INNER JOIN), строки из сохраненной таблицы или таблиц, для которых не найдено совпадение, добавляются в строки из VT2 в качестве внешних строк, генерируя VT3. Если в предложении FROM появляется более двух таблиц, шаги с 1 по 3 применяются повторно между результатом последнего соединения и следующей таблицей в предложении FROM, пока не будут обработаны все таблицы.

  4. ГДЕ: ГДЕ фильтр применяется к VT3. Только строки, для которых <where_condition>TRUE, вставляются в VT4.

  5. GROUP BY: строки из VT4 упорядочены в группы на основе списка столбцов, указанного в предложении GROUP BY. VT5 генерируется.

  6. КУБ | ROLLUP: супергруппы (группы групп) добавляются в строки из VT5, генерируя VT6.

  7. HAVING: фильтр HAVING применяется к VT6. Только группы, для которых <having_condition>TRUE, вставляются в VT7.

  8. SELECT: список SELECT обрабатывается, генерируя VT8.

  9. DISTINCT: повторяющиеся строки удаляются из VT8. VT9 генерируется.

  10. ORDER BY: строки из VT9 сортируются в соответствии со списком столбцов, указанным в предложении ORDER BY. Курсор генерируется (VC10).

  11. TOP: указанное количество или процент строк выбирается с начала VC10. Таблица VT11 генерируется и возвращается вызывающей стороне.



Следовательно, (INNER JOIN) ON будет фильтровать данные (количество данных VT будет само здесь уменьшаться) перед применением предложения WHERE. Последующие условия соединения будут выполняться с отфильтрованными данными, что повышает производительность. После этого только условие WHERE будет применять условия фильтра.

(Применение условных операторов в ON / WHERE не будет иметь большого значения в нескольких случаях. Это зависит от количества таблиц, к которым вы присоединились, и количества строк, доступных в каждой из таблиц объединения)

rafidheen
источник
10
«Поэтому (INNER JOIN) ON будет фильтровать данные (количество данных VT будет уменьшено здесь) до применения предложения WHERE». Не обязательно. Статья о логическом порядке обработки. Когда вы говорите, что конкретная реализация будет делать одну вещь перед другой, вы говорите о реализованном порядке обработки. Реализациям разрешено проводить любые оптимизации, которые им нравятся, при условии, что результат такой же, как если бы реализация следовала логическому порядку. Джо Селко много писал об этом в Usenet.
Майк Шеррилл 'Cat Recall'
@rafidheen "(INNER JOIN) ON будет фильтровать данные ... перед применением предложения WHERE ... что повышает производительность." Хорошая точка зрения. «После этого только условие WHERE будет применять условия фильтра». Как насчет предложения HAVING?
Джеймс
@James Это утверждение rafidheen неверно. Смотрите «оптимизация соединения» в руководстве. Также мои другие комментарии на этой странице. (И MikeSherrill'CatRecall's.) Такие «логические» описания описывают значение результата, а не то, как оно фактически рассчитывается. И такое поведение реализации не гарантируется, чтобы не измениться.
Philipxy
67

Синтаксис неявного соединения ANSI более старый, менее очевидный и не рекомендуется.

Кроме того, реляционная алгебра обеспечивает взаимозаменяемость предикатов в WHEREпредложении и INNER JOIN, таким образом, даже INNER JOINзапросы с WHEREпредложениями могут иметь предикаты, переупорядоченные оптимизатором.

Я рекомендую вам писать запросы наиболее читабельным способом.

Иногда это включает в себя создание INNER JOINотносительно «неполных» и включение некоторых критериев WHEREпросто для того, чтобы сделать списки критериев фильтрации более легкими в обслуживании.

Например, вместо:

SELECT *
FROM Customers c
INNER JOIN CustomerAccounts ca
    ON ca.CustomerID = c.CustomerID
    AND c.State = 'NY'
INNER JOIN Accounts a
    ON ca.AccountID = a.AccountID
    AND a.Status = 1

Написать:

SELECT *
FROM Customers c
INNER JOIN CustomerAccounts ca
    ON ca.CustomerID = c.CustomerID
INNER JOIN Accounts a
    ON ca.AccountID = a.AccountID
WHERE c.State = 'NY'
    AND a.Status = 1

Но это зависит, конечно.

Кейд Ру
источник
16
Ваш первый фрагмент определенно ранит мой мозг больше. Кто-нибудь на самом деле это делает? Если я встречаю кого-то, кто это делает, можно ли мне бить его по голове?
allyourcode
3
Я нахожу критерии, где это имеет смысл. Если я присоединяюсь к согласованной во времени поисковой таблице моментальных снимков (и у меня нет представления или UDF, обеспечивающего выбор допустимой даты), я включу дату вступления в силу в объединение, а не в WHERE, потому что оно меньше может быть случайно удален.
Каде Ру
14
@allyourcode: хотя этот тип синтаксиса объединения редко встречается в INNER JOIN, он довольно часто встречается для RIGHT JOIN и LEFT JOINS - указание большего количества деталей в предикате объединения устраняет необходимость в подзапросе и предотвращает непреднамеренное превращение внешних соединений. во внутренние соединения. (Хотя я согласен, что для ВНУТРЕННИХ СОЕДИНЕНИЙ я почти всегда помещал c.State = 'NY' в предложении WHERE)
Дейв Маркл
1
@allyourcode, я определенно так делаю! И я согласен с Кейдом .. Мне любопытно, есть ли достойная причина не
Арт
31

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

Использование явного соединения (ваш второй пример) намного удобнее для чтения и обслуживания.

Мэтт Б
источник
48
Я не мог не согласиться больше. Синтаксис JOIN чрезвычайно многословен и сложен в организации. У меня есть множество запросов, соединяющих 5, 10, даже 15 таблиц, использующих соединения WHERE, и они отлично читаются. Переписывание такого запроса с использованием синтаксиса JOIN приводит к искаженному беспорядку. Это говорит о том, что на этот вопрос нет правильного ответа, и что это зависит больше от того, с чем вам удобно.
Ной Йеттер
33
Ноа, я думаю, ты здесь в меньшинстве.
Мэтт б
2
Я получаю +1 к Мэтту и Ною. Мне нравится разнообразие :). Я вижу, откуда идет Ной; Внутреннее объединение не добавляет ничего нового в язык и определенно более многословно. С другой стороны, это может сделать ваше условие «где» намного короче, что обычно означает, что его легче читать.
allyourcode
5
Я предполагаю, что любая здравомыслящая СУБД переведет два запроса в один и тот же план выполнения; однако в действительности каждая СУБД отличается и единственный способ узнать наверняка - это проверить план выполнения (т. е. вам придется проверить его самостоятельно).
Matt B
Верно ли то, что @rafidheen предложил в другом ответе (тот, который содержит подробную последовательность выполнения SQL), что JOINs фильтруются по одному, уменьшая размер операций объединения по сравнению с полным декартовым объединением из 3 или более таблиц с ГДЕ фильтр применяется задним числом? Если это так, это предполагает, что JOIN предлагает улучшение производительности (а также преимущества в левом / правом соединениях, как также указано в другом ответе).
Джеймс
26

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

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

Позвольте мне указать вам на этот вопрос, чтобы понять, почему неявный синтаксис плох, если вы используете левые соединения. Sybase * = к стандарту Ansi с 2 разными внешними таблицами для одной внутренней таблицы

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

HLGEM
источник
3
@HLGEM: Хотя я полностью согласен с тем, что явные соединения лучше, в некоторых случаях вам просто нужно использовать старый синтаксис. Пример из реальной жизни: ANSI JOIN вошел в Oracle только в версии 9i, выпущенной в 2001 году, и только год назад (16 лет с момента опубликования стандарта) мне приходилось поддерживать несколько установок 8i, для которых мы имели выпускать критические обновления. Я не хотел поддерживать два набора обновлений, поэтому мы разработали и протестировали обновления для всех баз данных, включая 8i, что означало, что мы не могли использовать ANSI JOIN.
Quassnoi
+1 интересный момент, когда вы указываете, что синтаксис без INNER JOIN более подвержен ошибкам. Меня смущает ваше последнее предложение, когда вы говорите: «... стандарту, использующему явные объединения, 17 лет». поэтому вы предлагаете использовать ключевое слово INNER JOIN или нет?
Марко Демайо
1
@Marco Demaio, да, всегда используйте INNER JOIN или JOIN (это два одинаковых) или LEFT JOIN или RIGHT JOIN или CROSS JOIN и никогда не используйте неявные запятые соединения.
HLGEM
2
«Почему вы хотите написать код базы данных, которому [20 лет]?» - Я заметил, что вы пишете SQL с использованием HAVING"устаревшего", так как SQL начал поддерживать производные таблицы. Я также заметил, что вы не используете, NATURAL JOINхотя я бы сказал, что он INNER JOINустарел. Да, у вас есть свои причины (нет необходимости повторять их здесь!): Я хочу сказать, что те, кому нравится использовать более старый синтаксис, тоже имеют свои причины, и относительный возраст синтаксиса практически не имеет значения.
понедельник,
1
ГДЕ все еще в стандарте (покажи мне, где его нет). Итак, ничего устаревшего, судя по всему. Кроме того, «вместо исправления объединения» показывает мне разработчика, которого следует держаться подальше от СУБД в целом, далеко .
Юрген А. Эрхард
12

Они имеют различный человекочитаемый смысл.

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

Вы должны всегда кодировать, чтобы быть читаемым.

То есть, если это встроенные отношения, используйте явное соединение. если вы сопоставляете слабо связанные данные, используйте предложение where.

Джон Гитцен
источник
11

Стандарт SQL: 2003 изменил некоторые правила приоритета, поэтому оператор JOIN имеет приоритет над соединением «запятая». Это может фактически изменить результаты вашего запроса в зависимости от того, как он настроен. Это вызывает некоторые проблемы у некоторых людей, когда MySQL 5.0.12 перешел на соблюдение стандарта.

Так что в вашем примере ваши запросы будут работать одинаково. Но если вы добавили третью таблицу: SELECT ... FROM table1, table2 ПРИСОЕДИНЯЙТЕСЬ к table3 ON ... WHERE ...

До MySQL 5.0.12 сначала объединялись table1 и table2, затем table3. Теперь (5.0.12 и далее) сначала объединяются table2 и table3, затем table1. Это не всегда меняет результаты, но может и вы можете даже не осознавать этого.

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

Брент Бэйсли
источник
Стандартный SQL не изменился. MySQL был просто неправ, а теперь прав. Смотрите руководство по MySQL.
Филипп
4

Я знаю, что вы говорите о MySQL, но в любом случае: в Oracle 9 явные объединения и неявные объединения генерируют разные планы выполнения. AFAIK, который был решен в Oracle 10+: такой разницы больше нет.

Жоао Маркус
источник
1

Синтаксис соединения ANSI определенно более переносим.

Я собираюсь обновить Microsoft SQL Server и упомянуть, что синтаксис = * и * = для внешних объединений в SQL Server не поддерживается (без режима совместимости) для SQL Server 2005 и более поздних версий.

бензо
источник
2
Даже в SQL Server 2000 = и = могут давать неправильные результаты и никогда не должны использоваться.
HLGEM
2
*=и =*никогда не были ANSI и никогда не были хорошей нотацией. Вот почему ON был необходим - для ВНЕШНИХ СОЕДИНЕНИЙ в отсутствие подвыборов (которые были добавлены в то же время, так что они на самом деле не нужны в CROSS & INNER JOIN.)
philipxy
1

Если вы часто программируете динамические хранимые процедуры, вы влюбитесь в свой второй пример (используя where). Если у вас есть различные входные параметры и много морфов, то это единственный способ. В противном случае они оба будут выполнять один и тот же план запросов, поэтому в классических запросах явно нет явных различий.

Квиз Майстер
источник