Когда я начал писать запросы к базе данных, я еще не знал ключевое слово JOIN и, естественно, просто расширил то, что уже знал, и написал такие запросы:
SELECT a.someRow, b.someRow
FROM tableA AS a, tableB AS b
WHERE a.ID=b.ID AND b.ID= $someVar
Теперь, когда я знаю, что это то же самое, что ВНУТРЕННЕЕ СОЕДИНЕНИЕ, я нахожу все эти запросы в своем коде и спрашиваю себя, следует ли мне их переписать. Есть в них что-то вонючее или все в порядке?
Обновлено:
Резюме моего ответа: В этом запросе нет ничего плохого, НО использование ключевых слов, скорее всего, сделает код более читаемым / поддерживаемым.
Мой вывод: Я не буду менять свои старые запросы, но я исправлю свой стиль письма и буду использовать ключевые слова в будущем.
СПАСИБО за ответы!
Ваше резюме неверно, что делает ваш вывод плохим. Разница в запросах заключается в том, что ответ ниже будет выполнять предложение where для гораздо меньшего подмножества данных. Производительность намного выше, поэтому вам следует подумать о переписывании запросов с использованием внутренних соединений, где это возможно!
Принятый ответ Даниэля Спевака просто неверен. Они фантазируют о воплощении. Оптимизация для не внешних соединений a from на &, где синтаксис, тривиальна. В частности, для MySQL см. Официальную документацию по присоединиться к оптимизации и другим аспектам оптимизации).


В вашем примере нет ничего плохого в синтаксисе. Синтаксис «INNER JOIN» обычно называют синтаксисом «ANSI», и он появился после стиля, показанного в вашем примере. Он существует, чтобы прояснить тип / направление / составляющие соединения, но обычно функционально не отличается от того, что у вас есть.
Поддержка соединений «ANSI» является платформой для каждой базы данных, но в наши дни она более или менее универсальна.
Кстати, одним дополнением с синтаксисом «ANSI» было «ПОЛНОЕ ВНЕШНЕЕ СОЕДИНЕНИЕ» или «ПОЛНОЕ СОЕДИНЕНИЕ».
Надеюсь это поможет.
Я избегаю неявных объединений; когда запрос действительно большой, они затрудняют расшифровку кода
Благодаря явным объединениям и хорошему форматированию код становится более читаемым и понятным без комментариев.
+1 Да. А отсутствующий предикат соединения легче обнаружить. Синтаксис JOIN делает неверный оператор SQL некорректным, более очевидным является наличие проблемы.
Это скорее выбор синтаксиса. Я предпочитаю группировать свои условия соединения с моими соединениями, поэтому я использую синтаксис INNER JOIN
SELECT a.someRow, b.someRow
FROM tableA AS a
INNER JOIN tableB AS b
ON a.ID = b.ID
WHERE b.ID = ?
(? быть заполнителем)
Более подробные INNER JOIN, LEFT OUTER JOIN, RIGHT OUTER JOIN, FULL OUTER JOIN взяты из синтаксиса ANSI SQL / 92 для соединения. На мой взгляд, эта многословность делает объединение более ясным для разработчика / администратора баз данных о намерениях объединения.
И это упрощает обнаружение отсутствующего предиката соединения. Синтаксис JOIN заставляет неверный оператор выглядеть неправильно. Отсутствующий предикат соединения труднее обнаружить в синтаксисе старого стиля.
В SQL Server всегда есть планы запросов для проверки, текстовый вывод может быть сделан следующим образом:
SET SHOWPLAN_ALL ON
GO
DECLARE @TABLE_A TABLE
(
ID INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
Data VARCHAR(10) NOT NULL
)
INSERT INTO @TABLE_A
SELECT 'ABC' UNION
SELECT 'DEF' UNION
SELECT 'GHI' UNION
SELECT 'JKL'
DECLARE @TABLE_B TABLE
(
ID INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
Data VARCHAR(10) NOT NULL
)
INSERT INTO @TABLE_B
SELECT 'ABC' UNION
SELECT 'DEF' UNION
SELECT 'GHI' UNION
SELECT 'JKL'
SELECT A.Data, B.Data
FROM
@TABLE_A AS A, @TABLE_B AS B
WHERE
A.ID = B.ID
SELECT A.Data, B.Data
FROM
@TABLE_A AS A
INNER JOIN @TABLE_B AS B ON A.ID = B.ID
Сейчас я опущу план для создания табличной переменной, хотя план для обоих запросов идентичен:
SELECT A.Data, B.Data FROM @TABLE_A AS A, @TABLE_B AS B WHERE A.ID = B.ID
|--Nested Loops(Inner Join, OUTER REFERENCES:([A].[ID]))
|--Clustered Index Scan(OBJECT:(@TABLE_A AS [A]))
|--Clustered Index Seek(OBJECT:(@TABLE_B AS [B]), SEEK:([B].[ID]=@TABLE_A.[ID] as [A].[ID]) ORDERED FORWARD)
SELECT A.Data, B.Data FROM @TABLE_A AS A INNER JOIN @TABLE_B AS B ON A.ID = B.ID
|--Nested Loops(Inner Join, OUTER REFERENCES:([A].[ID]))
|--Clustered Index Scan(OBJECT:(@TABLE_A AS [A]))
|--Clustered Index Seek(OBJECT:(@TABLE_B AS [B]), SEEK:([B].[ID]=@TABLE_A.[ID] as [A].[ID]) ORDERED FORWARD)
Итак, короткий ответ - не нужно переписывать, если только вы не тратите много времени, пытаясь прочитать их каждый раз, когда поддерживаете их?
Это также зависит от того, выполняете ли вы только внутренние соединения таким образом или также внешние соединения. Например, синтаксис MS SQL Server для внешних соединений в предложении WHERE (= * и * =) может давать результаты, отличные от синтаксиса OUTER JOIN, и больше не поддерживается (http://msdn.microsoft.com/en-us/library/ms178653(SQL.90).aspx) в SQL Server 2005.
Фильтрация объединений с использованием только WHERE может быть крайне неэффективной в некоторых распространенных сценариях. Например:
SELECT * FROM people p, companies c
WHERE p.companyID = c.id AND p.firstName = 'Daniel'
Большинство баз данных будут выполнять этот запрос буквально, сначала беря Декартово произведение таблиц people и companies и фильтруя потом теми, у которых есть совпадающие поля companyID и id. Хотя полностью неограниченный продукт не существует нигде, кроме как в памяти, и то только на мгновение, его расчет действительно занимает некоторое время.
Лучше всего сгруппировать ограничения с JOIN там, где это необходимо. Это не только субъективно легче читать, но и намного эффективнее. Таким образом:
SELECT * FROM people p JOIN companies c ON p.companyID = c.id
WHERE p.firstName = 'Daniel'
Это немного длиннее, но база данных может смотреть на предложение ON и использовать его для вычисления полностью ограниченного JOIN напрямую, вместо того, чтобы начинать с все и затем ограничивать. Это быстрее вычисляется (особенно с большими наборами данных и / или объединениями многих таблиц) и требует меньше памяти.
Я меняю каждый запрос, который использует синтаксис «запятая JOIN». На мой взгляд, единственная цель его существования - лаконичность. Учитывая влияние на производительность, я не думаю, что это веская причина.
Можете ли вы привести примеры систем баз данных, которые выполняют декартовы произведения на основе старого синтаксиса соединения? Старый синтаксис достаточно распространен, и я думаю, что большинство оптимизаторов СУБД знают, что этого не следует делать.
Я также придерживаюсь того, что написал Майкл. Определенно существует потребность в [необходима цитата] внутри StackOverflow.
Попросите вашу СУБД объяснить вам оба утверждения. В хорошей системе старый SQL и синтаксис соединения SQL 1992 будут выполнять эквивалентные операции.
Вы говорите, что «большинство баз данных» генерируют «декартово произведение». Не так. Многие реляционные базы данных (DB2, Oracle, Sybase) предшествуют синтаксису SQL-92 JOIN. До этого все предикаты соединения находились в предложении WHERE. Отсутствующий предикат соединения является причиной декартовых произведений. Большинство баз данных выбирают эффективные пути доступа и выбирают «декартово произведение» только в том случае, если этот путь имеет наименьшую вычисленную стоимость (или если это был единственный доступный путь, или если инструкция была указана). Большим преимуществом синтаксиса SQL-92 JOIN является то, что более очевидно, что в инструкции отсутствует предикат соединения.
В целом:
Используйте ключевое слово JOIN для связывания (т. Е. «Соединения») первичных и внешних ключей.
Используйте предложение WHERE, чтобы ограничить набор результатов только интересующими вас записями.
Одна проблема, которая может возникнуть, - это когда вы пытаетесь смешать старое соединение «в стиле запятой» с соединениями SQL-92 в одном запросе, например, если вам нужно одно внутреннее соединение и другое внешнее соединение.
SELECT *
FROM table1 AS a, table2 AS b
LEFT OUTER JOIN table3 AS c ON a.column1 = c.column1
WHERE a.column2 = b.column2;
Проблема в том, что в последних стандартах SQL сказано, что JOIN оценивается перед соединением через запятую. Таким образом, ссылка на «a» в предложении ON дает ошибку, потому что имя корреляции еще не было определено, поскольку это предложение ON оценивается. Это очень запутанная ошибка.
Решение состоит в том, чтобы не смешивать два стиля соединений. Вы можете продолжать использовать стиль запятой в своем старом коде, но если вы напишете новый запрос, преобразуйте все объединения в стиль SQL-92.
SELECT *
FROM table1 AS a
INNER JOIN table2 AS b ON a.column2 = b.column2
LEFT OUTER JOIN table3 AS c ON a.column1 = c.column1;
Еще одна вещь, которую следует учитывать в старом синтаксисе соединения, заключается в том, что очень легко получить картционное соединение случайно, поскольку нет предложения on. Если ключевое слово Distinct присутствует в запросе и в нем используются объединения в старом стиле, преобразуйте его в стандартное соединение ANSI и посмотрите, нужно ли вам по-прежнему отдельное соединение. Если вы исправляете таким образом случайные картографические соединения, вы можете значительно улучшить производительность, переписав, чтобы указать соединение и поля соединения.
Стандарт ANSI SQL со временем меняется. Возможность выражать внешние соединения предшествовала введению синтаксиса предложения JOIN. Между прочим, синтаксис соединения предоставляет возможность индексации и подсказки соединения.