大多数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
现在显然当您需要外部联接时,需要第二种语法。但是,在进行内部联接时,为什么我应该首选第二种语法(反之亦然)?
大多数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
现在显然当您需要外部联接时,需要第二种语法。但是,在进行内部联接时,为什么我应该首选第二种语法(反之亦然)?
Answers:
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的部门。同样,如果使用旧样式的联接,您也可能会失去公司,如果该公司没有名称带有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是未来的潮流。
OUTER JOIN
语法*=
/ =*
/ *=*
弃用。
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)
第一种方法是较旧的标准。第二种方法是在SQL-92中引入的,网址为http://en.wikipedia.org/wiki/SQL。完整的标准可以在http://www.contrib.andrew.cmu.edu/~shadow/sql/sql1992.txt中查看。
数据库公司花了很多年才采用SQL-92标准。
因此之所以选择第二种方法,是根据ANSI和ISO标准委员会的SQL标准。
,
仍然是标准的。on
仅在引入子outer join
选择后才需要引入。
基本上,当FROM子句列出如下表时:
SELECT * FROM
tableA, tableB, tableC
结果是表A,B,C中所有行的叉积。然后应用限制WHERE tableA.id = tableB.a_id
,该限制将丢弃大量行,然后进一步... AND tableB.id = tableC.b_id
然后您应该只获取您真正感兴趣的行在。
DBMS知道如何优化此SQL,以便使用JOIN编写此SQL的性能差异可以忽略不计(如果有的话)。使用JOIN表示法使SQL语句更具可读性(恕我直言,不使用联接将语句弄得一团糟)。使用叉积,您需要在WHERE子句中提供连接条件,这就是表示法的问题。您在WHERE子句中挤满了诸如
tableA.id = tableB.a_id
AND tableB.id = tableC.b_id
仅用于限制叉积。WHERE子句应仅对结果集包含RESTRICTIONS。如果将表联接条件与结果集限制混合使用,您(和其他人)将发现您的查询更难阅读。您绝对应该使用JOIN并将FROM子句保留为FROM子句,并将WHERE子句保留为WHERE子句。
当您需要外部联接时,并不总是需要第二种语法:
甲骨文:
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
但是回到您的问题。我不知道答案,但它可能与一个事实,即加入更自然(语法,至少)比增加的表达到哪里,当你正在做的正是子句:加盟。
我听到很多人抱怨第一个太难理解了,不清楚。我没有发现任何问题,但是在进行了讨论之后,为了清楚起见,我甚至在INNER JOINS上也使用了第二个。