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

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

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

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


Обновлено:

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

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

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

Стандарт ANSI SQL со временем меняется. Возможность выражать внешние соединения предшествовала введению синтаксиса предложения JOIN. Между прочим, синтаксис соединения предоставляет возможность индексации и подсказки соединения.

Peter Wone 07.10.2008 05:09

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

achinda99 18.02.2009 01:13

Принятый ответ Даниэля Спевака просто неверен. Они фантазируют о воплощении. Оптимизация для не внешних соединений a from на &, где синтаксис, тривиальна. В частности, для MySQL см. Официальную документацию по присоединиться к оптимизации и другим аспектам оптимизации).

philipxy 05.09.2017 22:55
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
42
3
15 442
10
Перейти к ответу Данный вопрос помечен как решенный

Ответы 10

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

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

Кстати, одним дополнением с синтаксисом «ANSI» было «ПОЛНОЕ ВНЕШНЕЕ СОЕДИНЕНИЕ» или «ПОЛНОЕ СОЕДИНЕНИЕ».

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

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

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

+1 Да. А отсутствующий предикат соединения легче обнаружить. Синтаксис JOIN делает неверный оператор SQL некорректным, более очевидным является наличие проблемы.

spencer7593 29.05.2010 01:16

Это скорее выбор синтаксиса. Я предпочитаю группировать свои условия соединения с моими соединениями, поэтому я использую синтаксис 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 заставляет неверный оператор выглядеть неправильно. Отсутствующий предикат соединения труднее обнаружить в синтаксисе старого стиля.

spencer7593 29.05.2010 01:14

В 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». На мой взгляд, единственная цель его существования - лаконичность. Учитывая влияние на производительность, я не думаю, что это веская причина.

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

Michael Ratanapintha 30.09.2008 23:02

Я также придерживаюсь того, что написал Майкл. Определенно существует потребность в [необходима цитата] внутри StackOverflow.

Camilo Díaz Repka 20.02.2009 16:26

Попросите вашу СУБД объяснить вам оба утверждения. В хорошей системе старый SQL и синтаксис соединения SQL 1992 будут выполнять эквивалентные операции.

dlamblin 20.08.2009 20:21

Вы говорите, что «большинство баз данных» генерируют «декартово произведение». Не так. Многие реляционные базы данных (DB2, Oracle, Sybase) предшествуют синтаксису SQL-92 JOIN. До этого все предикаты соединения находились в предложении WHERE. Отсутствующий предикат соединения является причиной декартовых произведений. Большинство баз данных выбирают эффективные пути доступа и выбирают «декартово произведение» только в том случае, если этот путь имеет наименьшую вычисленную стоимость (или если это был единственный доступный путь, или если инструкция была указана). Большим преимуществом синтаксиса SQL-92 JOIN является то, что более очевидно, что в инструкции отсутствует предикат соединения.

spencer7593 29.05.2010 01:11

В целом:

Используйте ключевое слово 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 и посмотрите, нужно ли вам по-прежнему отдельное соединение. Если вы исправляете таким образом случайные картографические соединения, вы можете значительно улучшить производительность, переписав, чтобы указать соединение и поля соединения.

Другие вопросы по теме