显式与隐式SQL连接


399

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

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;

11
好问题。我很好奇为什么要使用显式联接。没有它就不可能进行所有查询吗?
安德鲁(Andrew)

6
使用EXPLAIN关键字来了解这两个查询的区别..使用JOIN,看到了差距。如果你在一个表中尝试超过10万条记录,你可以看到其中的差别...
Jeyanth库马尔

@andrew我的问题实际上是隐式联接是否是“ hack”的形式(如“涉及多个表的查询,未使用联接?那是hack吗?”)
bobobobo

3
它们是不同的,隐式连接在处理空值时会不时使您感到惊讶。使用显式联接并避免“什么都没有改变”时出现的错误。
BlackTigerX 2013年

1
没有区别。,CROSS JOIN用宽松的约束力和INNER JOINCROSS JOINONWHERE,但更紧密的结合。执行重要的是DBMS如何优化查询。
philipxy

Answers:


132

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

PS:请注意,IMPLICIT OUTER JOIN自SQL Server 2005起不赞成使用该IMPLICIT INNER JOIN语法。(仍支持问题中使用的语法)

弃用“旧样式” JOIN语法:只是部分事情


4
为了清楚起见,@ lomaxx是否可以指定不建议使用问题2中的哪种语法?
J Wynia

8
您可以提供证明文件吗?这在多个级别上听起来是错误的。
2009年

21
您如何弃用SQL标准?
David Crawshaw,2009年

7
@david Crenshaw,隐式联接已不在标准中,并且已经有18年了。
HLGEM 2010年

11
标准中保留了“内部”或“交叉”变体的所谓“隐式连接”。SQL Server将弃用从未成为标准的“旧式”外部联接语法(即*==*)。
2011年

129

就我个人而言,我更喜欢连接语法,因为它使表连接以及如何连接表更加清晰。尝试比较较大的SQL查询,从8个不同的表中进行选择,然后在where中进行大量筛选。通过使用连接语法,您可以将连接表的部分分离到要过滤行的部分。


4
我完全同意,但这有点题外话。OP询问效率。
villasv '17

56

在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行;table2有大约1000行。

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


这应该是公认的答案。这是正确的,计划是相同的(或与较大的语句接近),但是记录的数量过大,从而导致性能差异。
苏联边疆

37

第二种语法具有交叉联接的可能性:您可以将表添加到FROM部分,而无需相应的WHERE子句。这被认为是有害的。


如果from子句中的表名是从where子句中使用的表生成的,该怎么办?
2015年

您也可以使用显式的JOIN语法进行交叉联接。(stackoverflow.com/a/44438026/929164)您可能意味着它的严格性较弱,因此更容易出现用户错误。
丹尼尔·杜波夫斯基

15

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

我同意grom,您应该使用ANSI连接语法。正如他们所说,主要原因是为了清晰。而不是使用带有很多谓词的where子句,其中一些联接表和其他限制使用ANSI联接语法返回的行,而是使您盲目地弄清楚了哪些条件用于联接表,哪些条件用于限制表。结果。


5

在性能方面,它们是完全相同的(至少在SQL Server中是相同的),但是请注意,它们已经弃用了这种联接语法,并且sql server2005不支持此类型。

我认为您正在考虑不赞成使用的* =和= *运算符与“外部连接”。

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


5

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

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

具体来说,不支持外部联接(* =)。


2
坦白说,即使在SQL Server 2000中,我也不会使用它,* =语法经常给出错误的答案。有时,它们将这些解释为交叉联接。
HLGEM,2009年

2

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

您没有指定要使用的数据库,但是有可能表明SQL Server或MySQL没有什么实际的区别。


1
Leigh,您也可以在隐式连接中使用提示。
SquareCog

1
在Oracle中,联接顺序以有意义的方式影响执行计划的情况很少见。有关解释,请参见乔纳森·刘易斯的这篇文章
乔恩·海勒

1

正如Leigh Caldwell所说的那样,查询优化器可以根据功能上看起来像同一条SQL语句的内容生成不同的查询计划。有关此内容的更多信息,请查看以下两个博客文章:

Oracle Optimizer团队的一篇帖子

来自“结构化数据”博客的另一篇文章

希望您觉得这个有趣。


迈克,他们谈论的区别是您需要确保如果指定显式联接,则要指定要联接的联接条件,而不是过滤器。您会注意到,对于语义正确的查询,执行计划是相同的。
SquareCog

1

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


0

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

当处理内部联接时,可读性也没有真正的区别,但是,在处理左右联接时,它可能会变得复杂,因为在较旧的方法中,您会得到如下所示的信息:

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数据库中看到了两者的混合情况,这些数据库中有很多人在写它,无论是老年人还是年轻人。同样,它归结为脚本的可读性以及与您一起开发的团队的可读性。


-1

根据我的经验,使用子句中的交叉联接经常会导致大脑受损的执行计划,尤其是在使用Microsoft SQL产品的情况下。例如,SQL Server尝试估算表行数的方法非常可怕。使用内部联接语法使您可以控制查询的执行方式。因此,从实际的角度来看,鉴于当前数据库技术的简单性,您必须使用内部联接。


5
你有什么证明吗?因为接受的答案另有说明。
cimmanon
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.