SQL left join против нескольких таблиц из строки?

большинство диалектов SQL принимают оба следующих запроса:

SELECT a.foo, b.foo
FROM a, b
WHERE a.x = b.x

SELECT a.foo, b.foo
FROM a
LEFT JOIN b ON a.x = b.x

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

11 ответов


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

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

позвольте мне привести вам пример.

предположим, у вас есть 3 таблицы в вашей системе:

Company
Department
Employee

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

Итак, теперь вы хотите сделать следующее:

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

Итак, вы делаете это:

SELECT * -- for simplicity
FROM Company, Department, Employee
WHERE Company.ID *= Department.CompanyID
  AND Department.ID = Employee.DepartmentID

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

Итак, что происходит сейчас. Проблема в том, что это зависит от компонента database engine, оптимизатора запросов, индексов и статистики таблиц. Позвольте мне объяснить.

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

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

и в этом случае, из-за левого присоединения, отдел.Столбец ID будет иметь значение NULL, и поэтому, когда дело доходит до внутреннего соединения с сотрудником, нет способа выполнить это ограничение для сотрудника грести, а то не покажется.

С другой стороны, если оптимизатор запросов решает заняться отделом-сотрудник присоединиться первым, а затем сделать левый присоединиться к компаниям, вы увидите их.

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

введите новый синтаксис, с этим вы можете выбрать.

например, если вы хотите все компании, как описание проблемы заявлено, это то, что вы напишете:

SELECT *
FROM Company
     LEFT JOIN (
         Department INNER JOIN Employee ON Department.ID = Employee.DepartmentID
     ) ON Company.ID = Department.CompanyID

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

кроме того, предположим, вам нужны только отделы, содержащие букву X в своем имени. Опять же, со старым стилем вы рискуете потерять компанию, если у нее нет отделов с X в ее имени, но с новым синтаксисом вы можете сделать это:

SELECT *
FROM Company
     LEFT JOIN (
         Department INNER JOIN Employee ON Department.ID = Employee.DepartmentID
     ) ON Company.ID = Department.CompanyID AND Department.Name LIKE '%X%'

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

вот почему, среди других поставщиков, Microsoft устарела старый внешний синтаксис соединения, но не старый внутренний синтаксис соединения, так как SQL Server 2005 и выше. Единственный способ поговорить с базой данных, работающей на Microsoft SQL Server 2005 или 2008, используя синтаксис внешнего соединения старого стиля, - установить эту базу данных в режиме совместимости 8.0 (он же SQL Server 2000).

кроме того, старый способ, бросая кучу таблиц в оптимизатор запросов, с кучей предложений WHERE, был сродни тому, чтобы сказать: "Вот вы, сделайте все возможное". С новым синтаксисом оптимизатор запросов имеет меньше работы, чтобы выяснить, какие части пойти вместе.

так что у вас есть.

левое и внутреннее соединение-это волна будущего.


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

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

WHERE a.x = b.x(+)

или

WHERE a.x *= b.x

или

WHERE a.x = b.x or a.x not in (select x from b)

первый способ более старый стандарт. Второй метод был введен в SQL-92,http://en.wikipedia.org/wiki/SQL. Полный стандарт можно посмотреть наhttp://www.contrib.andrew.cmu.edu / ~shadow/sql/sql1992.txt .

потребовалось много лет, прежде чем компании базы данных приняли стандарт SQL-92.

поэтому причина, по которой второй метод является предпочтительным, это стандарт SQL в соответствии с комитетом стандартов ANSI и ISO.


второй предпочтительнее, потому что он гораздо менее вероятно приведет к случайному перекрестному соединению, забыв поставить предложение where. Предложение join with no on провалит проверку синтаксиса, предложение old style join with no where не провалится, оно выполнит перекрестное соединение.

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

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


в основном, когда ваше предложение FROM перечисляет таблицы так:

SELECT * FROM
  tableA, tableB, tableC

результатом является перекрестное произведение всех строк в таблицах A, B, C. Затем вы применяете ограничение WHERE tableA.id = tableB.a_id, который будет выбрасывать огромное количество строк, затем дальше ... AND tableB.id = tableC.b_id и вы должны получить только те строки, которые вас действительно интересуют.

DBMSs знают, как оптимизировать этот SQL, чтобы разница в производительности для записи этого с помощью соединений была незначительной (если таковая имеется). Используя присоединиться нотация делает оператор SQL больше читаемый (IMHO, не используя соединения, превращает оператор в беспорядок). Используя кросс-продукт, вам нужно предоставить критерии соединения в предложении WHERE, и в этом проблема с нотацией. Вы заполняете свой пункт WHERE такими вещами, как

    tableA.id = tableB.a_id 
AND tableB.id = tableC.b_id 

, который используется только для ограничения продукта кросс. Предложение WHERE должно содержать только ограничения для resultset. Если вы смешиваете критерии объединения таблиц с ограничениями resultset, вы (и другие) найдут ваш запрос более трудным для чтения. Вы обязательно должны использовать JOINs и сохранить предложение FROM a FROM и предложение WHERE a WHERE.


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

в действительно сложных операторах SELECT читателю становится намного легче понять, что происходит.


на SELECT * FROM table1, table2, ... синтаксис в порядке для нескольких таблиц, но он становится экспоненциально (не обязательно математически точное утверждение) все труднее и труднее читать по мере увеличения количества таблиц.

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

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


когда вам нужно внешнее соединение второй синтаксис не всегда требуется:

Oracle:

SELECT a.foo, b.foo
  FROM a, b
 WHERE a.x = b.x(+)

MSSQLServer (хотя это было устаревший в версии 2000) / Sybase:

SELECT a.foo, b.foo
  FROM a, b
 WHERE a.x *= b.x

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


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


для базы данных они в конечном итоге одинаковы. Для вас, однако, вам придется использовать этот второй синтаксис в некоторых ситуациях. Для редактирования запросов, которые в конечном итоге должны использовать его (выяснение, что вам нужно левое соединение, где у вас было прямое соединение), и для согласованности я бы шаблон только на 2-м методе. Это облегчит чтение запросов.


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