显式与隐式 SQL 联接

Explicit vs implicit SQL joins

提问人:dmanxiii 提问时间:9/5/2008 最后编辑:user8839064dmanxiii 更新时间:2/22/2023 访问量:225001

问:

显式与隐式内部连接是否存在效率差异? 例如:

SELECT * FROM
table a INNER JOIN table b
ON a.id = b.id;

与。

SELECT a.*, b.*
FROM table a, table b
WHERE a.id = b.id;
SQL 联接

评论

17赞 andrew 1/24/2011
问得好。我很好奇为什么要使用显式连接。没有它,就不可能做所有的查询吗?
7赞 Jeyanth Kumar 3/16/2012
使用 EXPLAIN 关键字来了解两个查询的区别。使用 JOIN 并查看差异..如果你尝试在一个超过100k条记录的表中,你可以看到差异......
0赞 bobobobo 4/13/2013
@andrew 我的问题实际上是隐式连接是否是一种“hack”形式(如“涉及多个表的查询,而不是使用连接?这是一个黑客,不是吗?
3赞 BlackTigerX 9/4/2013
它们是不同的,在处理 null 值时,隐式连接偶尔会让您感到惊讶;使用显式联接,避免在“未更改任何内容”时出现的错误!
4赞 philipxy 8/23/2017
没有区别。 具有较宽松的装订,并且具有类似但更紧密的装订。执行的关键是 DBMS 如何优化查询。,CROSS JOININNER JOINCROSS JOINONWHERE

答:

170赞 lomaxx 9/5/2008 #1

在性能方面,它们完全相同(至少在 SQL Server 中)。

PS:请注意,“隐式”语法 - 使用逗号或之后使用 - 自SQL Server 2005以来已弃用。(仍支持问题中使用的使用逗号的“implicit ()”语法。OUTER JOIN*==*WHERECROSSJOIN

弃用“旧式”JOIN 语法:只有部分内容

评论

8赞 NotMe 5/20/2009
你能提供证明文件吗?这在多个层面上听起来都是错误的。
26赞 David Crawshaw 9/30/2009
如何弃用 SQL 标准?
10赞 HLGEM 6/22/2010
@david Crenshaw 看来,隐式连接不再在标准中,并且已经有 18 年没有了。
4赞 J Wynia 9/5/2008
@lomaxx,为了清楚起见,您能否指定问题中 2 的哪种语法已被弃用?
13赞 onedaywhen 9/29/2011
“内部”或“交叉”变体的所谓“隐式连接”仍保留在标准中。SQL Server 正在弃用“旧式”外部连接语法(即 和 ),这从来都不是标准。*==*
159赞 grom 9/5/2008 #2

就我个人而言,我更喜欢连接语法,因为它可以更清楚地连接表以及它们是如何连接的。尝试比较较大的 SQL 查询,其中您从 8 个不同的表中进行选择,并且您在其中有很多过滤。通过使用联接语法,可以将联接表的部分与筛选行的部分分开。

评论

16赞 villasv 11/29/2017
我完全同意,但这有点跑题了。OP询问了效率。
6赞 Joshdan 9/5/2008 #3

在性能方面,它们完全相同(至少在 SQL Server 中),但请注意,它们正在弃用此联接语法,并且 SQL Server2005 不支持它。

我想您正在考虑已弃用的 *= 和 =* 运算符与“外部连接”。

我刚才测试了给定的两种格式,它们在 SQL Server 2008 数据库上正常工作。就我而言,他们产生了相同的执行计划,但我不能自信地说这永远是正确的。

3赞 Leigh Caldwell 9/5/2008 #4

在某些数据库(特别是 Oracle)上,联接的顺序会对查询性能产生巨大影响(如果有两个以上的表)。在一个应用程序中,在某些情况下,我们实际上有两个数量级的差异。使用内部连接语法可以控制这一点 - 如果您使用正确的提示语法。

您没有指定您正在使用的数据库,但概率建议 SQL Server 或 MySQL,在那里没有真正的区别。

评论

1赞 SquareCog 10/30/2008
Leigh,您也可以在隐式连接中使用提示。
1赞 Jon Heller 6/25/2013
在 Oracle 中,联接顺序以有意义的方式影响执行计划的情况极为罕见。有关解释,请参阅乔纳森·刘易斯(Jonathan Lewis)的这篇文章
7赞 deadbug 9/5/2008 #5

@lomaxx:澄清一下,我非常确定 SQL Serv 2005 支持上述两种语法。但是,不支持以下语法

select a.*, b.*  
from table a, table b  
where a.id *= b.id;

具体而言,不支持外部联接 (*=)。

评论

2赞 HLGEM 3/19/2009
坦率地说,即使在 SQL Server 2000 中我也不会使用它,*= 语法经常给出错误的答案。有时它会将这些解释为交叉连接。
17赞 andy47 9/7/2008 #6

您给出的第一个答案使用所谓的 ANSI 连接语法,另一个答案是有效的,可以在任何关系数据库中工作。

我同意格罗姆的观点,你应该使用ANSI连接语法。正如他们所说,主要原因是为了清晰。与其使用包含大量谓词的 where 子句(其中一些连接表,而另一些则限制使用 ANSI 连接语法返回的行),不如让您清楚地了解哪些条件用于连接表,哪些条件用于限制结果。

2赞 Mike McAllister 9/18/2008 #7

正如 Leigh Caldwell 所说,查询优化器可以根据功能上看起来像相同的 SQL 语句生成不同的查询计划。如需进一步阅读,请查看以下两篇博文:-

来自 Oracle Optimizer 团队的一篇文章

“结构化数据”博客的另一篇帖子

我希望你觉得这很有趣。

评论

0赞 SquareCog 10/30/2008
Mike,他们谈论的区别在于,您需要确保,如果您指定了显式联接,则指定了要联接的联接条件,而不是过滤器。您会注意到,对于语义正确的查询,exec 计划是相同的。
46赞 edosoft 11/25/2008 #8

第二种语法具有交叉联接的不必要可能性:您可以在没有相应 WHERE 子句的情况下将表添加到 FROM 部分。这被认为是有害的。

评论

0赞 Jus12 9/3/2015
如果 from 子句中的表名是从 where 子句中使用的表生成的,该怎么办?
1赞 Daniel Dror 1/2/2020
您也可以使用显式 JOIN 语法进行交叉连接。(stackoverflow.com/a/44438026/929164) 您的意思可能是它不那么严格,因此更容易出现用户错误。
1赞 David 12/1/2011 #9

性能方面,它应该没有任何区别。显式连接语法对我来说似乎更干净,因为它清楚地定义了 from 子句中表之间的关系,并且不会弄乱 where 子句。

74赞 Matt Fenwick 4/25/2012 #10

在MySQL 5.1.51上,两个查询具有相同的执行计划:

mysql> explain select * from table1 a inner join table2 b on a.pid = b.pid;
+----+-------------+-------+------+---------------+------+---------+--------------+------+-------+
| id | select_type | table | type | possible_keys | key  | key_len | ref          | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+--------------+------+-------+
|  1 | SIMPLE      | b     | ALL  | PRIMARY       | NULL | NULL    | NULL         |  986 |       |
|  1 | SIMPLE      | a     | ref  | pid           | pid  | 4       | schema.b.pid |   70 |       |
+----+-------------+-------+------+---------------+------+---------+--------------+------+-------+
2 rows in set (0.02 sec)

mysql> explain select * from table1 a, table2 b where a.pid = b.pid;
+----+-------------+-------+------+---------------+------+---------+--------------+------+-------+
| id | select_type | table | type | possible_keys | key  | key_len | ref          | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+--------------+------+-------+
|  1 | SIMPLE      | b     | ALL  | PRIMARY       | NULL | NULL    | NULL         |  986 |       |
|  1 | SIMPLE      | a     | ref  | pid           | pid  | 4       | schema.b.pid |   70 |       |
+----+-------------+-------+------+---------------+------+---------+--------------+------+-------+
2 rows in set (0.00 sec)

table1有 166208 行; 大约有 1000 行。table2

这是一个非常简单的案例;它绝不能证明查询优化器不会混淆并在更复杂的情况下生成不同的计划。

评论

1赞 SovietFrontier 5/14/2019
这应该是公认的答案。这是正确的,计划是相同的(或接近更大的语句),但记录量会很大,从而导致性能差异。
0赞 Ciro Santilli OurBigBook.com 9/17/2022
postgresql.org/docs/14/explicit-joins.html“Controlling the Planner with Explicit Clauses”有一些注释可能会引起 PostgreSQL 的兴趣。另外:dba.stackexchange.com/questions/198182/......JOIN
-1赞 Sean 8/14/2015 #11

根据我的经验,使用交叉联接和 where-clause 语法通常会产生脑损伤的执行计划,尤其是在使用 Microsoft SQL 产品时。例如,SQL Server 尝试估计表行计数的方式非常可怕。使用内部联接语法可以对查询的执行方式进行一些控制。因此,从实际的角度来看,鉴于当前数据库技术的返祖性质,您必须使用内部连接。

评论

5赞 cimmanon 8/14/2015
你有什么证据吗?因为公认的答案不是这样。
2赞 Michele La Ferla 5/2/2019 #12

基本上,两者之间的区别在于,一个是用旧方式写的,而另一个是用现代方式写的。就我个人而言,我更喜欢使用内部、左侧、外部、右侧定义的现代脚本,因为它们更具解释性,并使代码更具可读性。

在处理内部连接时,可读性也没有真正的区别,但是,在处理左右连接时可能会变得复杂,因为在旧方法中,你会得到这样的结果:

SELECT * 
FROM table a, table b
WHERE a.id = b.id (+);

以上是左连接的旧写法,而不是以下内容:

SELECT * 
FROM table a 
LEFT JOIN table b ON a.id = b.id;

正如您直观地看到的,脚本的现代编写方式使查询更具可读性。(顺便说一句,右连接也是如此,外部连接稍微复杂一些)。

回到样板,SQL编译器以相同的方式处理查询,因此查询的编写方式没有区别。我在 Oracle 数据库中看到了两者的混合,有很多人写进去,无论是年长的还是年轻的。同样,它归结为脚本的可读性以及您正在开发的团队。