大多数SQL方言都接受以下两个查询:

SELECT a.foo, b.foo
FROM a, b
WHERE a.x = b.x

SELECT a.foo, b.foo
FROM a
LEFT JOIN b ON a.x = b.x


现在显然当您需要外部联接时,需要第二种语法。但是在进行内部联接时,为什么我应该选择第二种语法而不是第一种(反之亦然)?

评论

古法:你是怎么找到的?尽管我的问题比“如何做”更是最佳实践

由于这是最佳做法,请将该维基设为Wiki。

我认为没有人评论这两者的性能。任何人都可以确认或引用任何有关重大差异的合理理由吗?

@ahnbizcad给定的两个查询没有做相同的事情。第一个返回的内容与INNER JOIN ON相同。实现是特定于DBMS版本的,即使这样,也无法保证。但是,DBMS转换等效于逗号vs. INNER JOIN ON / WHERE与CROSS JOIN WHERE的情况是微不足道的。了解有关关系数据库查询的优化/实现。

有资源推荐?我尝试从这里学习的原因是庞大而密集的手册。

#1 楼

在大多数现代数据库中,仅列出表并使用WHERE子句指定连接条件的旧语法已被弃用。

不只是为了展示,旧语法还有可能当在同一查询中同时使用INNER和OUTER联接时,会变得模棱两可。

让我举个例子。

假设您的系统中有3个表:

Company
Department
Employee


每个表包含许多行,这些行链接在一起。您有多个公司,每个公司可以有多个部门,每个部门可以有多个员工。

好,所以现在您要执行以下操作:


列出所有公司,包括其所有部门和所有员工。请注意,有些公司还没有任何部门,但请确保也将其包括在内。确保只检索有员工的部门,但始终列出所有公司。


因此,请执行以下操作:

SELECT * -- for simplicity
FROM Company, Department, Employee
WHERE Company.ID *= Department.CompanyID
  AND Department.ID = Employee.DepartmentID


请注意,最后一个是内部联接,以便满足您只希望部门与人相处的条件。

好,那么现在发生了什么。好吧,问题在于,它取决于数据库引擎,查询优化器,索引和表统计信息。让我解释一下。

如果查询优化器确定这样做的方法是先成立公司,然后找到部门,然后与员工进行内部联接,那么您就不会获取没有部门的任何公司。

原因是WHERE子句确定最终结果中的行而不是行的各个部分。

在这种情况下,由于左联接,Department.ID列将为NULL,因此,当涉及到Employee的INNER JOIN时,就无法满足Employee行的约束,因此它赢得了不会出现。

另一方面,如果查询优化器决定先解决部门雇员的合并问题,然后再与公司进行左联接,您将看到它们。

因此旧语法含糊不清。无法处理查询提示就无法指定所需的内容,并且某些数据库根本没有办法。

输入新的语法,您可以选择新的语法。

例如,如果您想要所有公司,如问题描述中所述,您将这样写:

SELECT *
FROM Company
     LEFT JOIN (
         Department INNER JOIN Employee ON Department.ID = Employee.DepartmentID
     ) ON Company.ID = Department.CompanyID


这里您指定您想要部门雇员联接作为一个联接完成,然后与公司左联接。

另外,假设您只希望名称中包含字母X的部门。同样,如果使用旧样式的联接,那么如果公司的名称中没有任何部门,但如果使用新语法,您也可能会失去公司的风险:

SELECT *
FROM Company
     LEFT JOIN (
         Department INNER JOIN Employee ON Department.ID = Employee.DepartmentID
     ) ON Company.ID = Department.CompanyID AND Department.Name LIKE '%X%'


这个额外的子句用于连接,但不是整个行的过滤器。因此,该行可能与公司信息一起出现,但该行的所有部门和雇员列中都可能有NULL,因为该公司的名称中没有任何部门带有X。使用旧语法很难做到这一点。

这就是为什么在其他供应商中,Microsoft从SQL Server 2005及更高版本开始不赞成使用旧的外部联接语法,而不推荐使用旧的内部联接语法。使用旧风格的外部联接语法与运行在Microsoft SQL Server 2005或2008上的数据库进行通讯的唯一方法是将该数据库设置为8.0兼容模式(又名SQL Server 2000)。

另外,旧的方式是通过在查询优化器中抛出一堆表以及一堆WHERE子句,类似于说“在这里,尽力而为”。使用新的语法,查询优化器只需花费很少的工作就可以弄清楚哪些部分是一起使用的。

所以就可以了。

LEFT和INNER JOIN是未来的潮流。

评论


“在大多数现代数据库中已被弃用。” ---只是好奇,哪个?

–zerkms
2011年4月12日,1:13

原谅我,我对* =运算符不熟悉,它有什么作用?谢谢!

–ultrajohn
2012年5月16日13:25

Star =和= Star是(很明显)左右外部联接,还是左右联接?自古以来就不推荐使用,自SQL Server 6起我就没有使用它们。

–托尼·霍普金森
2012年5月21日,0:56



不建议使用逗号。不推荐使用从不标准的OUTER JOIN语法* = / = * / * = *。

–philipxy
15年8月22日在12:48

当然可以,因为不赞成使用较旧的语法,因此您不应该使用它。不推荐使用它的原因与外部联接有关,但在此过程中它们不推荐使用整个连接。我认为这很重要。如果您有特定问题,欢迎您提供自己的答案或编辑我的答案。关于查询优化器的工作要做得很少,该部分取自msdn上的一篇旧文章,我没有链接。

–拉瑟·V·卡尔森(Lasse V. Karlsen)
17年5月13日在11:01

#2 楼

JOIN语法将条件保持在适用于其的表附近。当连接大量表时,此功能特别有用。

也可以使用第一种语法进行外部连接:

WHERE a.x = b.x(+)




WHERE a.x *= b.x




WHERE a.x = b.x or a.x not in (select x from b)


评论


* =语法在MS SQLServer中已被弃用,这是有充分的理由的:它不仅使阅读变得更困难,而且并没有像人们认为的那样起作用,并且与外观类似的LEFT JOIN不同。 (+)语法对我来说并不熟悉;那是什么SQL实现?

–Euro Micelli
09年5月21日在19:09

至少Oracle使用了另一种语法。

–拉瑟·V·卡尔森(Lasse V. Karlsen)
09年5月21日在19:15

永远不要使用SQL Server语法* =,它不会给出一致的结果,因为它有时会解释为交叉联接而不是左联接。即使在SQL Server 2000之前,也是如此。如果有任何使用此代码,则需要进行修复。

–HLGEM
09年5月27日在13:23

#3 楼

第一种方法是较旧的标准。第二种方法是在SQL-92中引入的,网址为http://en.wikipedia.org/wiki/SQL。完整的标准可以在http://www.contrib.andrew.cmu.edu/~shadow/sql/sql1992.txt中查看。

数据库公司采用SQL-92花费了很多年。标准。

所以之所以选择第二种方法的原因是,它是ANSI和ISO标准委员会的SQL标准。

评论


,仍然是标准的。一旦引入了子选择,仅需要为外部联接引入on。

–philipxy
17年12月17日在9:33

#4 楼

基本上,当FROM子句列出如下表时:

SELECT * FROM
  tableA, tableB, tableC


结果是表A,B,C中所有行的叉积。然后,应用限制WHERE tableA.id = tableB.a_id将会丢弃大量行,然后进一步... AND tableB.id = tableC.b_id,然后您应该只获得您真正感兴趣的那些行。

DBMS知道如何优化此SQL,以便使用JOIN编写此脚本的性能差异可以忽略不计(如果有的话)。使用JOIN表示法使SQL语句更具可读性(恕我直言,不使用联接会使该语句陷入混乱)。使用叉积,您需要在WHERE子句中提供连接条件,这就是表示法的问题。您在WHERE子句中挤满了诸如

    tableA.id = tableB.a_id 
AND tableB.id = tableC.b_id 


之类的东西,仅用于限制叉积。 WHERE子句应仅对结果集包含RESTRICTIONS。如果将表联接条件与结果集限制混合使用,您(和其他人)将发现您的查询更难阅读。您绝对应该使用JOIN并将FROM子句保留为FROM子句,并将WHERE子句保留为WHERE子句。

#5 楼

首选第二种方法,因为它很可能因忘记放置where子句而导致意外的交叉联接。没有on子句的联接将失败语法检查,没有no子句的旧式联接不会失败,它将进行交叉联接。

此外,当您以后必须进行左联接时,它它们都在同一结构中,有助于维护。而且旧的语法自1992年以来就已经过时,现在已经是停止使用它的时候了。

此外,我发现许多专门使用第一种语法的人并不真正理解join和了解连接对于查询时获得正确的结果至关重要。

#6 楼

我认为在此页面上采用第二种方法(使用显式JOIN)有很多充分的理由。但最关键的是,当从WHERE子句中删除JOIN条件时,在WHERE子句中查看剩余的选择条件将变得更加容易。

在真正复杂的SELECT语句中,对于读者来说变得更加容易了解发生了什么。

#7 楼

SELECT * FROM table1, table2, ...语法对于几个表是可以的,但是随着表数量的增加,它变得越来越难指数化(不一定是数学上准确的语句)。

JOIN语法较难编写(在一开始),但是它使它清楚地表明哪些标准会影响哪些表。这将使出错变得更加困难。

此外,如果所有联接都是INNER,则两个版本都是等效的。但是,当您在语句中的任何位置有OUTER连接时,事情就会变得更加复杂,并且几乎可以保证您所写的内容不会查询您认为的内容。

#8 楼

当您需要外部联接时,并不总是需要第二种语法:

Oracle:

SELECT a.foo, b.foo
  FROM a, b
 WHERE a.x = b.x(+)


MSSQLServer(尽管在2000版本中已弃用) )/ Sybase:

SELECT a.foo, b.foo
  FROM a, b
 WHERE a.x *= b.x


,但返回您的问题。我不知道答案,但是这可能与以下事实有关:连接比在进行此操作时向where子句添加表达式更自然(至少在语法上):

评论


SQL Server已经弃用了左联接语法,即使在SQL Server 2000中,它也不会始终如一地给出正确的结果(有时会执行交叉联接而不是左联接),并且永远不要在SQL Server中使用。

–HLGEM
09年5月21日在19:04

@HLGEM:感谢您提供信息。我将更新我的帖子以反映您的意思。

– Pablo Santa Cruz
09年5月21日在19:32

#9 楼

我听到很多人抱怨第一个太难理解了,不清楚。我没有发现任何问题,但是在进行了讨论之后,为了清楚起见,我甚至在INNER JOINS上也使用了第二个。

评论


我养成了不使用JOIN语法并以第一方式进行操作的习惯。我必须承认我经常仍然习惯于这种习惯,只是因为我认为我的大脑已经适应了这种逻辑,对我而言,有时连接语法似乎很难考虑。

–TheTXI
09年5月21日在18:58

我也被这样教过。我更改了编码风格,因为人们会看着它,而又不容易意识到发生了什么。由于没有逻辑上的区别,我找不到选择前者的理由,所以我觉得我应该适应使代码更清晰,以帮助其他人理解我写的东西。

–kemiller2002
09年5月21日在19:02

#10 楼

对于数据库,它们最终是相同的。但是,对于您来说,在某些情况下必须使用第二种语法。为了编辑最终不得不使用它的查询(发现您需要在具有直连接的地方使用左连接),并且为了保持一致性,我仅在第二种方法上进行模式化。这将使阅读查询变得更加容易。

#11 楼

那么第一个查询和第二个查询可能会产生不同的结果,因为LEFT JOIN包含了第一个表中的所有记录,即使右表中没有相应的记录。