SQL JOIN:USING、ON或WHERE有什么区别吗?

52

我想知道在这些联接语句中,SQL执行方式是否有任何区别:

SELECT * FROM a,b WHERE a.ID = b.ID

SELECT * FROM a JOIN b ON a.ID = b.ID

SELECT * FROM a JOIN b USING(ID)

这两种写法有性能差异吗?或者算法上的差异?

还是只是语法糖?


4
如果可能的话,我总是使用'ON'来清楚地表明连接条件。在一个很长的查询中,where子句可能与连接语句相隔甚远,这让你不知道它的用途。 - Marc B
你尝试过使用explain命令查看查询评估计划吗? - Pirooz
可能是[SQL INNER JOIN问题]的重复问题(https://dev59.com/Uk_Ta4cB1Zd3GeqPB5A8) - Conrad Frix
尽管被频繁问到,但引发了良好的讨论,点个赞。 - gbn
只是提醒一下 - 在 SQL Server 中不支持使用 USING 关键字进行等值连接。USING 关键字用于告诉查询批处理在批处理中触发查询时要使用哪个数据库。 - RBT
显示剩余2条评论
6个回答

56

性能上没有区别。

然而,第一种风格是ANSI-89,有些商店会让你丢掉它。包括我的。第二种风格是ANSI-92,更加清晰明了。

示例:

哪个是JOIN,哪个是过滤器?

FROM T1,T2,T3....
WHERE T1.ID = T2.ID AND
     T1.foo = 'bar' AND T2.fish = 42 AND
     T1.ID = T3.ID

FROM T1 
   INNER JOIN T2 ON T1.ID = T2.ID
   INNER JOIN T3 ON T1.ID = T3.ID
WHERE
   T1.foo = 'bar' AND T2.fish = 42

如果你使用了外连接(=*, *=),那么第二种风格将按照预期工作。第一种方式可能不会,而且在SQL Server 2005+中也已经被弃用。
ANSI-92风格更难出错。如果你忘记了一个条件,使用旧风格很容易导致笛卡尔积(交叉连接)。而使用ANSI-92,你将得到语法错误。
编辑:进一步澄清
- 不使用"join the where"(隐式)的原因是外连接结果不可靠。 - 如果你使用显式的外连接 + 隐式内连接,你仍然会得到不可靠的结果,并且在使用上存在不一致性。
这不仅仅是语法问题,而是关于拥有一个语义正确的查询。
编辑,2011年12月 SQL Server逻辑查询处理顺序是FROM、ON、JOIN、WHERE...,因此,如果你混合使用"隐式WHERE内连接"和"显式FROM外连接",你很可能不会得到预期的结果,因为查询是不明确的...

ANSI-92取代了ANSI-89?实际上,这是关于清晰度和可维护性的问题。您似乎反对JOIN语法... - gbn
2
@Yochai Timmer,我最近查看的时候是92比89。 - JonH
1
“哪个是连接符,哪个是过滤器?”这些术语是主观的,因此问题是有偏见的:如果没有看到完整的查询并了解其目的,就无法回答。对于INNER JOIN,可以说没有区别。如果它们是OUTER JOINs,则将(您暗示的)过滤条件更改为连接条件可能会基于NULL更改查询结果,但我无法确定这是否是“错误”的,而不知道设计者的意图。 - onedaywhen
@onedaywhen 你的意思不是在 WHERE 子句中经常“过滤”由 ON 子句产生的 NULL 值吗? - ScottEdwards2000
1
@ScottEdwards2000:收到,我不应该尝试使用口语或鼓励他人这样做! - onedaywhen
显示剩余5条评论

4

我讨厌使用WHERE强制连接,这看起来很不好,就像一个肮脏的hack。正确的ANSI连接方式是使用ON:

SELECT 
    p.Product,
    o.Order
FROM 
    Product p
INNER JOIN
    Order o
ON
    o.OrderID = p.OrderID

在连接时,最好使用ON,而使用WHERE来过滤结果。请记住,WHERE是在分组和按所需排序之后,用于筛选结果的最后一步操作。因此,不应该使用WHERE来连接表格,因为这样会使代码难以阅读。

SELECT 
    p.Product,
    o.Order
FROM 
    Product p
INNER JOIN
    Order o
ON
    o.OrderID = p.OrderID
WHERE
    o.Category = 'IT'

最终,你(开发者)可能不会在未来存在,因此可读性和可维护性将有助于接管你的代码的人 :)

当我看到开发者使用WHERE来连接他们的表时,这通常意味着他们不了解足够的T-SQL。 这是我的个人观点。


1
OP并没有询问您是否使用ANSI连接语法是否“漂亮”,而是关于实现可能存在的差异。 - jeroenh
2
@jeroenh - 我认为我的帖子清楚地澄清了他的问题。 - JonH
8
@jeroenh说的不是关于漂亮的问题,而是关于清晰度、可维护性和使用已经标准化近20年的东西。+1。我们难道不在这里教授和鼓励最佳实践吗? - gbn
1
我完全同意适当的JOIN语法更易读和易维护,编辑后我撤回了我的投票反对。但是我仍然坚持我的立场:这个问题涉及可能的性能/算法差异,而不是语法。所以这仍然没有回答问题。@oJmHo,我们可以进行一场体面的讨论吗?而不是互相称呼对方的名字? - jeroenh
1
@jeroenh - 没问题,我并不是在说其中一个在性能方面比另一个更好。这是正确的做法,就像早上起床后洗澡一样是正确的一样 :). 你可以不洗澡,但有人会闻到你的气味。同样,使用适当的编码规范是正确的,否则有人会从好代码中闻到坏代码的味道。 - JonH
显示剩余2条评论

4

区别在于可读性和可维护性。 SELECT * FROM a JOIN b ON a.ID = b.ID 可以准确传达您的意图,所有信息都在同一个地方。

我不能确定,因为我没有深入了解最后一个查询优化器,但我非常自信,您看到的差异在性能上是微不足道的,如果有的话。


3

目前还没有任何人提供关于 USING(...) 语法的答案。

虽然这两个查询在逻辑上和大多数现代优化器的角度来看是相等的:

SELECT * FROM a, b WHERE a.id = b.id
SELECT * FROM a JOIN b ON a.id = b.id

这个有稍微不同的语义:

SELECT * FROM a JOIN b USING (id)

假设以下架构:
CREATE TABLE a (id int, a1 int, a2 int);
CREATE TABLE b (id int, b1 int, b2 int);

前两个查询将展开它们的星号:

SELECT a.id, a.a1, a.a2, b.id, b.a1, b.a2 FROM ...

第三个查询将会把它的星号扩展为:
SELECT coalesce(a.id, b.id) AS id, a.a1, a.a2, b.a1, b.a2 FROM ...

这与许多原因有关,其中包括:
  • 预计的列数从6变为5。 这可能会在使用UNION或其他集合运算时遇到问题。 而您可能不需要这样做,只需使用星号。
  • 不再有限定(和重复)的a.idb.id列,仅有一个id列。 尽管 PostgreSQL 仍允许对id进行修饰性引用(例如,在需要消除歧义时),但是 Oracle 不支持。
  • 因此,在具有USING(...)语法的第三个查询中,在 Oracle 中不再可以投影a.*b.*


0

这是一个与SO问题显式 vs 隐式 SQL 连接重复的问题。一般来说,我认为隐式连接(where 版本)是不好的形式,也不如显式连接(on 版本)清晰。我还认为隐式连接正在被淘汰,但这个不确定。不过两者的执行计划是相同的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接