SQL离开连接与FROM行上的多个表?

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

SELECT a.foo, b.foo FROM a, b WHERE ax = bx SELECT a.foo, b.foo FROM a LEFT JOIN b ON ax = bx 

现在显然,当你需要一个外连接时,第二个语法是必需的。 但是,当做一个内部连接,为什么我应该更喜欢第二个语法的第一个(反之亦然)?

在大多数现代数据库中,旧的语法只是列出表格,并使用WHERE子句来指定连接标准。

这不仅仅是为了演示,在同一个查询中同时使用INNER和OUTER连接时,旧的语法可能会变得模糊不清。

让我举一个例子。

假设你的系统中有3个表格:

 Company Department Employee 

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

好的,所以现在你想要做到以下几点:

列出所有的公司,包括他们所有的部门和所有的员工。 请注意,有些公司还没有任何部门,但也要确保包含这些部门。 确保你只检索有雇员的部门,但总是列出所有的公司。

所以你这样做:

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

请注意,最后一个有一个内部联接,为了满足你只希望有人的部门的标准。

好的,现在发生了什么事。 那么问题是,它依赖于数据库引擎,查询优化器,索引和表统计信息。 让我解释。

如果查询优化器确定这样做的方法是首先要找一家公司,然后find这些部门,然后与员工进行内部联接,那么不会有任何没有部门的公司。

其原因是WHERE子句决定哪些最终结果,而不是行的单个部分。

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

另一方面,如果查询优化器决定首先解决部门 – 员工join,然后与公司左键join,则会看到它们。

所以旧的语法是不明确的。 没有办法指定你想要的,而不处理查询提示,有些数据库根本就没有办法。

input新的语法,您可以select。

例如,如果你想要所有的公司,正如问题描述所说的,这就是你要写的东西:

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

在这里您指定要将部门 – 员工连接作为一个连接来完成,然后将这些连接的结果留给公司。

另外,假设您只需要在名称中包含字母X的部门。 同样,如果旧风格的联合,你也有失去公司的风险,如果它没有任何名称中有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%' 

这个额外的子句用于join,但不是整个行的filter。 所以这行可能会与公司信息一起出现,但在该行的所有部门和员工列中可能都有空值,因为没有任何部门在其公司名称中包含X. 旧的语法很难。

这就是为什么在其他供应商中,微软自从SQL Server 2005及更高版本以来已经弃用了旧的外部连接语法,而不是旧的内部连接语法。 使用旧式外连接语法与Microsoft SQL Server 2005或2008上运行的数据库交谈的唯一方法是在8.0兼容模式(又名SQL Server 2000)中设置该数据库。

另外,老式的方法,通过在查询优化器中抛出一堆表,以及一堆WHERE子句,就好像在说“在这里,尽你所能”。 使用新的语法,查询优化器为了弄清楚哪些部分汇集在一起​​,所做的工作量较less。

所以你有它。

左和内联是未来的潮stream。

JOIN语法保持它们所应用的表格附近的条件。 当你join大量的表格时,这是特别有用的。

顺便说一下,你也可以用第一个语法来做外连接:

 WHERE ax = bx(+) 

要么

 WHERE ax *= bx 

要么

 WHERE ax = bx or ax not in (select x from b) 

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

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

所以第二种方法之所以首选,是根据ANSI和ISO标准委员会的SQL标准。

第二个是首选,因为通过忘记放在where子句中导致意外交叉连接的可能性要小得多。 没有on子句的连接将会失败语法检查,旧式连接没有where子句不会失败,它会做一个交叉连接。

此外,当您稍后必须进行左连接时,维护时它们都处于相同的结构中。 自从1992年以来,旧的语法已经过时,停止使用它已经过去了。

另外,我发现很多专门使用第一种语法的人并不真正理解连接,理解连接对查询时得到正确的结果是至关重要的。

基本上,当你的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子句。

我认为在这个页面上有一些很好的理由来采用第二种方法 – 使用显式的JOIN。 尽pipe如此,当从WHERE子句中删除JOIN条件时,在WHERE子句中查看其余select条件变得更加容易。

在非常复杂的SELECT语句中,读者更容易理解正在发生的事情。

对于一些SELECT * FROM table1, table2, ...语法是可以的,但是随着表的数量的增加,它变得成倍地( 不一定是math上精确的语句 )越来越难读。

JOIN语法很难写(在开始处),但它明确了什么标准影响哪些表。 这使得错误更难。

另外,如果所有连接都是INNER,那么两个版本都是等价的。 但是,当你在语句中的任何地方joinOUTER时,情况会变得更加复杂,这实际上保证了你写的内容不会查询你的想法。

当你需要一个外连接时,第二个语法并不总是需要的:

甲骨文:

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

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

 SELECT a.foo, b.foo FROM a, b WHERE ax *= bx 

但回到你的问题。 我不知道答案,但是这可能与以下事实有关:当您正在执行连接时, 连接更自然(至less在语法上),而不是将expression式添加到where子句。

我听到很多人抱怨,第一个太难理解了,不清楚。 我没有看到它的问题,但经过讨论后,为了清晰起见,我甚至在INNER JOINS上使用第二个。

对于数据库来说,它们最终是一样的。 但是,对于你来说,在某些情况下你必须使用第二种语法。 为了编辑查询,最终不得不使用它(找出你需要一个左连接,你有一个直接连接),为了一致性,我只会在第二种方法模式。 这将使阅读查询更容易。

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