是否使用JOIN关键字


45

以下SQL查询是相同的:

SELECT column1, column2
FROM table1, table2
WHERE table1.id = table2.id;

SELECT column1, column2
FROM table1 JOIN table2 
ON table1.id = table2.id;

肯定会在我尝试过的每个DBMS上产生相同的查询计划。

但是我经常听到或听到这样的观点:一个绝对比另一个更好。当然,这些主张从不带有任何解释。

在我工作的地方,第二版似乎受到了大多数其他开发人员的青睐,因此我也倾向于这种风格,以最大程度地减少意外。但是在我的心中,我真的在想第一个(因为这是我最初学习它的方式)。

这些形式中的一种在客观上是否优于另一种?如果没有,那么为什么一个使用另一个?


1
为什么不分析它,让我们其他人知道结果?一般而言,性能远远超过了样式偏好。
Demian Brecht

3
“在我尝试过的每个DBMS上都得出相同的查询计划。”如果可以从性能上回答这个问题,那就在stackoverflow.com上询问它。las,它们是相同的查询。
SingleNegationElimination

啊..错过了:)
Demian Brecht

2
“主观”并不表示“您的意见”。我已经编辑这一种满足在中列出的标准FAQ
亚伦诺特,2011年

我也倾向于这种风格,以最大程度地减少惊喜,我想您只是回答了自己的问题。惊喜是坏的。
Pieter B

Answers:


60

我发现第二种形式更好。我承认这可能是因为我是从中学到的,但是我确实有一个具体的原因-分离关注点。将用于联接表的字段放在where子句中可能会导致难以理解查询。

例如,执行以下查询:

select *
from table1, table2, table3, table4
where table1.id = table2.id
and table2.id = table3.id
and table3.id = table4.id
and table1.column1 = 'Value 1'

上面的查询将表连接条件和实际业务逻辑条件组合到一个空间中。对于大型查询,这可能很难理解。

但是,现在使用以下代码:

select *
from table1 join table2 on table1.id = table2.id
join table3 on table2.id = table3.id
join table4 on table3.id = table4.id
where table1.column1 = 'Value 1'

在这种情况下,所有与表或表之间的关系都与from子句隔离,而用于查询限制的实际业务逻辑在where子句中。我认为这更容易理解,尤其是对于较大的查询。


这是唯一明智的方法,特别是当您超过两个表或需要左,右和完全联接的组合时。
aglassman 2014年

5
+1“的关注点分离”加入带来的数据一起,where子句决定你感兴趣的数据子集。

39

联接语法在1992年替代了旧的逗号语法。当前没有理由使用逗号语法编写代码。您一无所获,并且会遇到一些显式语法根本没有的问题。

首先,当您遇到更复杂的查询时,很容易通过丢失where条件来进行意外的交叉联接。这是显式联接语法可以防止发生的事情,因为您将收到语法错误。

如果您打算进行交叉联接,则显式联接语法将使这一点变得清晰,而在隐式语法中,进行维护的人可能会认为您忘记添加where子句。

然后是左联接和右联接的问题,这在使用隐式语法的至少某些数据库中是有问题的。它们在SQL Server中已弃用,实际上,即使在较旧的版本中,它们也无法真正返回正确的结果。需要外部联接的任何查询都不应在SQL Server中包含隐式语法。

此外,我在这里和其他站点上看到了一些问题,当人们混合使用隐式和显式联接(例如,添加左联接)时,会出现错误的结果,因此混合它们是一个糟糕的主意。

最后,许多使用隐式联接的人实际上并不了解联接。这是您必须有效查询数据库所必须具备的关键理解。


谢谢你的解释。教我时,我们看到了两种语法,但是没有解释区别。有时,我有时设法生成缺少位置查询的查询,坦率地说,仅在显式连接中,这样做会增加写入量。
awiebe

8

哈。在查看PostgreSQL文档时,我碰巧找到了自己的问题的可能答案。总结一下此页面所解释的内容,结果查询仍然是相同的,但是优化器必须考虑的计划数量会随着联接数量的增加而呈指数增长。

在进行大约六个这样的联接之后,数量如此之大,以至于计划查询的时间可能很明显,大约十次之后,优化器将从详尽的计划搜索切换到概率搜索,并且可能无法达到最佳计划。

通过设置运行时参数,可以指示计划者将显式提及的内部联接和交叉联接与隐式联接区别对待,将其强制到计划的顶部,而不探索其他选择。

值得注意的是,两种情况下的默认行为都是相同的,并且获取替代计划需要了解dbms的内部知识以及所涉及表的特性,才能获得不同的结果


2
但是,您对这些文档有一些误解。首先,实际上有三个阈值。正如您所指出的那样,您将触发GEQO;其他两个(来自和崩溃极限)最终使计划者不得不选择适用的索引,而不是重新组织联接顺序。其次,同样重要的是,查询在解析时会被重写。这导致第一个示例查询被解析为与第二个查询树完全相同的查询树-然后阈值让PG知道是否应该尝试重新排序联接。
Denis de Bernardy 2011年

8

好吧,这是它的集合论视图:

当使用逗号分隔两个(或多个)表名时,您要使用的是笛卡尔乘积。“左”表的每一行都将与右表的每一行“匹配”(串联)在一起。

现在,如果您在where子句中编写某些内容,就像在此“连接”上放置条件,告诉要与哪些行“连接”的行。

实际上,这实际上是“连接”行:),因此,join关键字有助于提供更易读的语法,并且更容易理解您“确实”想要连接某些通用值。类似于@Dustin在上面澄清的内容。

现在,每个DBMS都很聪明,即,它不会先计算笛卡尔乘积,然后再过滤掉数据(极其浪费),而是根据查询结构进行计算。我唯一能想到的是,当您要求它“加入”时,这就像使加入活动变得明确,并且可能有助于更快地运行代码(减少多少?您必须对其进行概要分析并查看),但是在以逗号分隔的情况,需要一些时间来“确定”最佳策略。我可能是错的,但我只是在对如何编码进行过有根据的猜测...


5

我认为通常在这种情况下使用JOIN语句会更好。

如果将来出现需要将语句从INNER JOIN更改为OUTER JOIN的情况,则使用第二个语句将更容易。


3

就执行而言,任何RDBMS都将使它们成为同一件事。它取决于一个人是否更具可读性和表达力。

使用JOIN可以清楚地知道什么是联接匹配和什么是实际选择,如:

select name, deptname
from people p, departments d
where p.deptid = d.id and p.is_temp = 'Y'

select name, deptname
from people p
    inner join departments d on p.deptid = d.id
where p.is_temp = 'Y'

后一种情况立即表明哪个是连接条件,哪个是选择标准。


1

我仅一次见过这两种结果导致了一组不同的优化,并且如果内存有效,则它是在真正毛茸茸的查询中的ms-sql2k中。在该示例中,与* =一起使用的旧表格的性能提高了约4倍。包括我们的Microsoft技术人员在内的任何人都无法解释原因。MS家伙将其标记为错误。我再也见不到。

由于大多数RDBMS都足够聪明,无法执行全部笛卡尔运算,所以我能想到的不使用它的最大原因(除了它已贬值外)是与我一起工作的30-35岁以下的大多数人从未见过以前的旧表格,遇到它们时就会迷路。


当然,左联接语法永远无法可靠地提供正确的结果(请参阅SQL Server 2000的BOL),因此即使速度更快,我也会替换掉它。
HLGEM 2014年

我从未遇到过这种情况,并且使用星号进行的搜索永远不会很好地结束,您是否有示例?
条例草案

-1

旧样式已被弃用,您不应使用它。

甚至不应该争论哪个更好还是不更好。新代码不应使用旧语法。


我认为这个答案实际上并没有增加任何内容,而不必说为什么它已被弃用并且不应使用。
RemcoGerlich 2015年

1
@RemcoGerlich为什么它已被弃用在这里没有讨论。这里讨论的是使用旧语法还是新语法。一个人好于另一个人没有意义:您不应该使用旧语法。该为什么问题是另一个讨论。(已经在20年前解决了。)
Pieter B

-4

使用更简洁的语法的原因之一是它更简洁,因此,如果您对它感到满意,它更容易阅读。我认为冗长的情况类似于在COBOL中编写算术运算,例如乘以C乘以A。


Downvoters:此回应中是否有任何事实上的错误,还是他们只是“不同意” downvotes?
亚当·利布沙(AdamLibuša)
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.