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

时间:2009-05-21 18:53:57

标签: sql syntax join

大多数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

现在很明显,当您需要外连接时,需要第二种语法。但是在进行内连接时,为什么我更喜欢第二种语法(或反之亦然)?

11 个答案:

答案 0 :(得分:303)

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

不仅仅是show,当你在同一个查询中同时使用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,因此当涉及INNER JOIN to Employee时,无法实现Employee行的约束,因此它不会出现。

另一方面,如果查询优化器决定首先处理部门 - 员工加入,然后与公司进行左联接,您将看到它们。

所以旧语法含糊不清。如果不处理查询提示,就没有办法指定你想要的东西,而且有些数据库根本没办法。

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

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

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%'

此附加子句用于连接,但不是整行的过滤器。因此,该行可能会显示公司信息,但该行的所有部门和员工列中可能都包含NULL,因为该公司的名称中没有X部门。使用旧语法很难。

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

此外,通过在查询优化器中抛出一堆表以及一堆WHERE子句,旧方法类似于“在这里,尽你所能”。使用新语法,查询优化器只需要做很少的工作就可以确定哪些部分组合在一起。

所以你有它。

LEFT和INNER JOIN是未来的潮流。

答案 1 :(得分:16)

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)

答案 2 :(得分:11)

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

数据库公司采用SQL-92标准需要很多年。

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

答案 3 :(得分:10)

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

SELECT * FROM
  tableA, tableB, tableC

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

DBMS知道如何优化此SQL,以便使用JOIN编写此代码的性能差异可以忽略不计(如果有的话)。使用JOIN表示法使SQL语句更多可读(恕我直言,不使用连接将语句变成一团糟)。使用交叉产品,您需要在WHERE子句中提供连接条件,这是符号的问题。你正在用

之类的东西挤占你的WHERE子句
AND tableB.id = tableC.b_id

仅用于限制交叉产品。 WHERE子句应该只包含结果集的RESTRICTIONS。如果将表连接条件与结果集限制混合使用,则您(和其他人)会发现您的查询难以阅读。您绝对应该使用JOIN并将FROM子句保留为FROM子句,并将WHERE子句保留为WHERE子句。

答案 4 :(得分:9)

第二个是首选,因为忘记放入where子句会导致意外交叉连接的可能性大大降低。没有on子句的连接将无法进行语法检查,没有where子句的旧式连接不会失败,它将进行交叉连接。

此外,当您稍后需要左连接时,维护它们都在相同的结构中是有帮助的。自1992年以来,旧的语法已经过时,现在已经过时了,停止使用它。

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

答案 5 :(得分:6)

我认为这个页面有一些很好的理由采用第二种方法 - 使用显式JOIN。但是,当从WHERE子句中删除JOIN条件时,更容易看到WHERE子句中的剩余选择条件。

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

答案 6 :(得分:5)

SELECT * FROM table1, table2, ...语法适用于几个表,但随着表数的增加,它会成倍地呈指数级(不一定是数学上准确的语句)。

JOIN语法更难编写(在开头),但它明确了哪些条件会影响哪些表。这使得犯错误更加困难。

此外,如果所有连接都是INNER,则两个版本都是等效的。然而,当你在声明中的任何地方加入OUTER时,事情会变得更复杂,而且几乎可以保证你所写的内容不会查询你认为你写的内容。

答案 7 :(得分:2)

当您需要外连接时,第二种语法总是需要:

甲骨文:

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

MSSQLServer(尽管在2000版本中它是deprecated)/ Sybase:

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

但回到你的问题。我不知道答案,但这可能与 join 比在 where 子句中添加表达式更自然(语法上至少)这一事实有关当你这样做时:加入

答案 8 :(得分:0)

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

答案 9 :(得分:0)

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

答案 10 :(得分:0)

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