为什么使用JOIN子句而不是WHERE条件?

时间:2009-10-23 13:08:03

标签: sql oracle

我针对Oracle数据库开发。当我需要手动编写(不使用像hibernate这样的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

我从OLD oracle DBA那里学到了这种风格。我从那时起就知道这不是标准的SQL语法。除了非标准和更少的数据库便携性,使用这种格式还有其他影响吗?

10 个答案:

答案 0 :(得分:16)

我不喜欢这种风格,因为它更难确定哪些WHERE子句用于模拟JOINs以及哪些用于实际过滤器,我不喜欢使用它的代码不必要地难以确定程序员的原始意图。

答案 1 :(得分:10)

我遇到这种格式的最大问题是忘记了一些join 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

轰!笛卡尔积! :)

答案 2 :(得分:7)

在某些情况下,旧式连接是错误的(外连接是罪魁祸首)。虽然它们在使用内部联接时或多或少相等,但它们可能会在外部联接时生成不正确的结果,尤其是外部的列可以为null时。这是因为在使用较旧的语法时,连接条件不是逻辑评估,直到构造了整个结果集,根本不可能在连接外侧的列上表示条件当列可以为null时将过滤记录,因为没有匹配的记录。

举个例子:

选择 所有 客户,以及8月份处理发票的所有发票中小部件的销售总额(Invoice.ProcessDate为 Null)

使用新的ANSI-92 Join语法

 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子句中的所有条件,所有未处理的发票行将被添加回到最终的结果集......所以这对于旧的语法是不可能的 - 任何试图用空的处理日期过滤掉发票的东西都将消除客户......唯一的选择是使用相关的子查询。

答案 3 :(得分:6)

有些人会说这种风格的可读性较差,但这是习惯问题。从性能的角度来看,这并不重要,因为查询优化器会处理这个问题。

答案 4 :(得分:4)

  

我从那时起就知道这不是标准的SQL语法。

这不是真的。 "a,b where"语法来自ansi-89标准,"a join b on"语法是ansi-92。但是,89语法已弃用,这意味着您将其用于新查询。

此外,在某些情况下,较旧的样式缺乏表达能力,特别是在外部联接或复杂查询方面。

尝试挑选连接条件的where子句可能会很痛苦。对于任何不止一个加入的东西,旧的风格绝对是邪恶的。一旦你了解了新的风格,你也可以继续使用它。

答案 5 :(得分:3)

这是一种标准的SQL语法,只是旧的标准而不是JOIN。有一个原因是语法已经发展,你应该使用更新的JOIN语法,因为:

  1. 它更具表现力,清楚地表明哪些表是JOINed,JOIN顺序,哪些条件适用于哪个JOIN,并将过滤WHERE条件与JOIN条件分开。

  2. 它支持LEFT,RIGHT和FULL OUTER JOIN,WHERE语法不支持。

  3. 我认为你不会发现WHERE类型的JOIN比JOIN语法的可移植性差得多。

答案 6 :(得分:3)

只要你不使用ANSI自然连接功能,我就可以了。

我发现这个引用 - ScottCher,我完全同意:

我发现WHERE语法比INNER JOIN更容易阅读 - 我猜它就像Vegemite。世界上大多数人可能觉得它很恶心,但是孩子们喜欢吃它喜欢它。

答案 7 :(得分:3)

这实际上取决于习惯,但我总是发现Oracle的逗号分隔语法更自然。第一个原因是我认为使用(INNER)JOIN 会降低可读性。第二是关于灵活性。最后,根据定义,连接是笛卡尔积。您不一定必须根据两个表的ID限制结果。虽然很少,但很可能需要两张桌子的笛卡尔积。根据ID限制它们只是一种非常合理的做法,但 NOT A RULE 。但是,如果您使用 JOIN 关键字,例如SQL Server,它不会让你省略 ON 关键字。假设您要创建组合列表。你必须这样做:

SELECT *
FROM numbers
JOIN letters
ON 1=1

除此之外,我发现Oracle的(+)语法也非常合理。这是一个很好的方式,“将此记录添加到结果集中,即使它是null。”它比 RIGHT / LEFT JOIN 语法更好,因为事实上没有左或右!当你想要连接10个具有几种不同类型的外连接的表时,会让人感到困惑,哪个表位于“左侧”,哪个表位于右侧。

顺便说一句,作为更一般的评论,我认为SQL可移植性不再存在于实际世界中。标准SQL非常差,并且经常需要多种DBMS特定语法的表达性,我不认为100%可移植SQL代码是可实现的目标。我观察到的最明显的证据是好的旧 行号 问题。只需搜索任何论坛的“ sql行号”,包括SO,你会看到数百篇帖子询问如何在特定的DBMS中实现。与此相似且相关, 限制返回的行数 ,例如..

答案 8 :(得分:0)

这是Transact SQL语法,我不太确定它是如何“不可移植”的 - 它是Sybase中使用的主要语法,例如(Sybase支持ANSI语法)以及许多其他数据库(如果不是全部)。

ANSI语法的主要好处是它允许你编写一些T-SQL禁止的相当棘手的链式连接

答案 9 :(得分:-2)

实际上,这种语法更多可以移植到JOIN,因为它几乎适用于任何数据库,而不是每个人都支持JOIN语法(例如Oracle Lite不支持[除非这个]最近发生了变化])。

相关问题