Что-то не так с соединениями, которые не используют ключевое слово JOIN в SQL или MySQL? - PullRequest
42 голосов
/ 24 сентября 2008

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

SELECT a.someRow, b.someRow 
FROM tableA AS a, tableB AS b 
WHERE a.ID=b.ID AND b.ID= $someVar

Теперь, когда я знаю, что это то же самое, что и ВНУТРЕННЕЕ СОЕДИНЕНИЕ, я нахожу все эти запросы в своем коде и спрашиваю себя, следует ли мне их переписать. Есть ли в них что-то вонючее или они просто в порядке?


EDIT:

Сводка моего ответа : В этом запросе нет ничего плохого, НО использование ключевых слов, скорее всего, сделает код более читабельным / поддерживаемым.

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

СПАСИБО за ответы!

Ответы [ 10 ]

36 голосов
/ 24 сентября 2008

Фильтрация объединений исключительно с использованием WHERE может быть крайне неэффективной в некоторых распространенных сценариях. Например:

SELECT * FROM people p, companies c 
    WHERE p.companyID = c.id AND p.firstName = 'Daniel'

Большинство баз данных выполнят этот запрос буквально, сначала взяв декартово произведение из таблиц people и companies и , а затем с фильтрацией по тем, которые имеют companyID и id поля. В то время как полностью неограниченный продукт не существует нигде, кроме как в памяти и только на мгновение, его расчет занимает некоторое время.

Лучший подход - сгруппировать ограничения с JOIN там, где это уместно. Это не только субъективно легче читать, но и намного эффективнее. Thusly:

SELECT * FROM people p JOIN companies c ON p.companyID = c.id
    WHERE p.firstName = 'Daniel'

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

Я изменяю каждый запрос, который использует синтаксис "запятая JOIN". На мой взгляд, единственной целью его существования является лаконичность. Учитывая влияние на производительность, я не думаю, что это веская причина.

10 голосов
/ 24 сентября 2008

Более подробные INNER JOIN, LEFT OUTER JOIN, RIGHT OUTER JOIN, FULL OUTER JOIN взяты из синтаксиса ANSI SQL / 92 для объединения. Для меня это многословие делает объединение более ясным для разработчика / администратора базы данных о том, что намерение с объединением.

6 голосов
/ 24 сентября 2008

В 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)

Итак, короткий ответ - Не нужно переписывать, если вы не тратите много времени, пытаясь прочитать их каждый раз, когда ведете их?

5 голосов
/ 24 сентября 2008

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

SELECT a.someRow, b.someRow
FROM tableA AS a
INNER JOIN tableB AS b
  ON a.ID = b.ID
WHERE b.ID = ?

(? Быть заполнителем)

4 голосов
/ 18 февраля 2009

В старом синтаксисе соединения следует учитывать еще одну особенность: очень легко получить случайное соединение, поскольку в нем нет условия on. Если ключевое слово Distinct находится в запросе и использует соединения старого стиля, преобразуйте его в стандартное соединение ANSI и посмотрите, нужны ли вам еще отличия. Если вы исправляете случайные объединения карт, вы можете значительно повысить производительность, переписав, указав поля объединения и объединения.

4 голосов
/ 24 сентября 2008

Единственная проблема, которая может возникнуть, - это когда вы пытаетесь смешать старое объединение в стиле «запятая» с объединениями 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;
4 голосов
/ 24 сентября 2008

В целом:

Используйте ключевое слово JOIN для связи (т. Е. "Присоединение") первичных ключей и внешних ключей.

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

4 голосов
/ 24 сентября 2008

С вашим синтаксисом в вашем примере все в порядке. Синтаксис INNER JOIN обычно называется синтаксисом ANSI и появился после стиля, проиллюстрированного в вашем примере. Он существует для уточнения типа / направления / составляющих объединения, но в целом не отличается функционально от того, что у вас есть.

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

В качестве дополнительного примечания, одним дополнением с синтаксисом 'ANSI' было 'FULL OUTER JOIN' или 'FULL JOIN'.

Надеюсь, это поможет.

3 голосов
/ 24 сентября 2008

я избегаю неявных объединений; когда запрос действительно большой, они затрудняют расшифровку кода

С явными объединениями и хорошим форматированием код становится более читабельным и понятным без комментариев.

2 голосов
/ 24 сентября 2008

Это также зависит от того, выполняете ли вы просто внутренние соединения или внешние соединения. Например, синтаксис MS SQL Server для внешних объединений в предложении WHERE (= * и * =) может давать результаты, отличные от синтаксиса OUTER JOIN, и больше не поддерживается (http://msdn.microsoft.com/en-us/library/ms178653(SQL.90).aspx) в SQL Server 2005.

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