Зачем использовать предложение JOIN вместо условия WHERE? - PullRequest
12 голосов
/ 23 октября 2009

Я разрабатываю против баз данных Oracle. Когда мне нужно написать вручную (а не использовать ORM, такой как спящий режим), я использую условие WHERE вместо JOIN.

например (это просто для иллюстрации стиля):

Select *
from customers c, invoices i, shipment_info si
where c.customer_id = i.customer_id
    and i.amount > 999.99 
    and i.invoice_id = si.invoice_id(+)  -- added to show a replacement for a join
order by i.amount, c.name

Я выучил этот стиль у ОДНОГО Оракула DBA. С тех пор я узнал, что это не стандартный синтаксис SQL. Кроме того, что он нестандартен и гораздо менее переносим для базы данных, есть ли другие последствия использования этого формата?

Ответы [ 10 ]

16 голосов
/ 23 октября 2009

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

10 голосов
/ 23 октября 2009

Самая большая проблема, с которой я столкнулся при использовании этого формата, - это тенденция забыть какое-то предложение WHERE для какого-либо соединения, в результате чего получается декартово произведение. Это особенно распространено (по крайней мере для меня) при добавлении новой таблицы в запрос. Например, предположим, что в микс добавлен стол ADDRESSES, а ваш ум немного забывчив:

SELECT *
  FROM customers c, invoices i, addresses a
 WHERE c.customer_id = i.customer_id
   AND i.amount > 999.99
 ORDER BY i.amount, c.name

Boom! Декартово произведение! :)

7 голосов
/ 23 октября 2009

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

Как пример:

Выберите всех Клиентов и сумму продаж Виджетов по всем их Счетам за август, где Счет был обработан (Invoice.ProcessDate: Не Ноль)

с использованием нового синтаксиса соединения ANSI-92

 Select c.name, Sum(d.Amount)
 From customer c
    Left Join Invoice I 
        On i.custId = c.custId
            And i.SalesDate Between '8/1/2009' 
                      and '8/31/2009 23:59:59'
            And i.ProcessDate Is Not Null
    Left Join InvoiceDetails d 
        On d.InvoiceId = i.InvoiceId
            And d.Product = 'widget'
 Group By c.Name

Попробуйте сделать это со старым синтаксисом ... Поскольку при использовании синтаксиса старого стиля все условия в предложении where оцениваются / применяются ПЕРЕД добавлением «внешних» строк, все строки необработанного счета будут добавлены обратно в окончательный набор результатов ... Так что это невозможно при старом синтаксисе - все, что пытается отфильтровать счета с нулевыми обработанными датами, устранит клиентов ... единственная альтернатива - использовать коррелированный подзапрос.

6 голосов
/ 23 октября 2009

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

4 голосов
/ 23 октября 2009

С тех пор я узнал, что это не стандартный синтаксис SQL.

Это не совсем так. Синтаксис "a,b where" взят из стандарта ansi-89, синтаксис "a join b on" - ansi-92. Однако синтаксис 89 устарел , что означает, что вы должны не использовать его для новых запросов.

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

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

3 голосов
/ 23 октября 2009

Это действительно зависит от привычек, но я всегда находил синтаксис Oracle с запятыми более естественным. Первая причина в том, что я думаю, что использование (INNER) JOIN снижает читабельность. Второе о гибкости. В конце концов, объединение является декартовым произведением по определению. Вам не обязательно ограничивать результаты на основе идентификаторов обеих таблиц. Хотя это очень редко, вполне может понадобиться декартово произведение двух таблиц. Ограничение их на основе идентификаторов - это очень разумная практика, но НЕ ПРАВИЛО . Однако, если вы используете ключевое слово JOIN , например, в. SQL Server, он не позволит вам опустить ключевое слово ON . Предположим, вы хотите создать комбинированный список. Вы должны сделать так:

SELECT *
FROM numbers
JOIN letters
ON 1=1

Кроме того, я считаю, что синтаксис Oracle (+) также очень разумен. Это хороший способ сказать: « Добавить эту запись также в набор результатов, даже если она нулевая. » Это намного лучше, чем синтаксис RIGHT / LEFT JOIN , потому что факта нет ни левого ни правого! Когда вы хотите объединить 10 таблиц с несколькими различными типами внешних объединений, возникает путаница, какая таблица находится «слева», а какая справа.

Кстати, в качестве более общего комментария я не думаю, что переносимость SQL больше существует в практическом мире. Стандартный SQL настолько плох, и столь часто требуется выразительность разнообразного специфического синтаксиса СУБД, я не думаю, что 100% переносимый код SQL является достижимой целью. Самым очевидным доказательством моего наблюдения является старый добрый номер строки проблемный. Просто поищите в любом форуме « sql row number» , включая SO, и вы увидите сотни сообщений, спрашивающих, как этого можно достичь в конкретной СУБД. Аналогично и в связи с этим, ограничивает количество возвращаемых строк , например ..

3 голосов
/ 23 октября 2009

Пока вы не используете функцию естественного соединения ANSI, я в порядке.

Я нашел эту цитату - ScottCher, я полностью согласен:

Мне кажется, что синтаксис WHERE легче читать, чем INNER JOIN - я думаю, он похож на Vegemite. Большинство людей в мире, вероятно, находят это отвратительным, но дети, воспитанные, едят это, любят это.

3 голосов
/ 23 октября 2009

Это стандартный синтаксис SQL, просто старше стандарта, чем JOIN. Есть причина, по которой синтаксис развился, и вы должны использовать более новый синтаксис JOIN, потому что:

  1. Это более выразительно, четко указывает, какие таблицы JOINed, порядок JOIN, какие условия применяются к какому JOIN, и отделяет условия фильтрации WHERE от условий JOIN.

  2. Поддерживаются ВЛЕВО, ВПРАВО и ПОЛНОЕ ВНЕШНЕЕ СОЕДИНЕНИЕ, чего нет в синтаксисе WHERE.

Не думаю, что вы найдете JOIN типа WHER существенно менее переносимым, чем синтаксис JOIN.

0 голосов
/ 23 октября 2009

Это синтаксис Transact SQL, и я не совсем уверен, насколько он «непереносим» - это основной синтаксис, используемый в Sybase, например (Sybase поддерживает синтаксис ANSI), а также во многих других базах данных (если не все).

Основным преимуществом синтаксиса ANSI является то, что он позволяет вам писать довольно сложные цепочки соединений, которые T-SQL запрещает

0 голосов
/ 23 октября 2009

На самом деле, этот синтаксис является более переносимым, чем JOIN, потому что он будет работать практически с любой базой данных, тогда как не все поддерживают синтаксис JOIN (например, Oracle Lite не поддерживает недавно изменился]).

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