在阅读它之后,这不是显式与隐式SQL连接的副本。 答案可能相关(甚至相同),但问题是不同的。


它们之间有什么不同?每一种都应该有什么不同?

如果我正确地理解了这个理论,那么查询优化器应该能够互换地使用这两种方法。


当前回答

为了获得更好的性能,表应该有一个用于join的特殊索引列。

所以如果你的条件列不是那些索引列之一,那么我怀疑最好把它保存在WHERE。

所以你使用索引列来JOIN,然后在JOIN之后你在无索引列上运行条件。

其他回答

我认为这种区别可以通过SQL中操作的逻辑顺序来最好地解释,这是简化的:

FROM(包括连接) 在哪里 集团 聚合 有 窗口 选择 截然不同的 并,相交,除 命令 抵消 获取

联接不是select语句的子句,而是FROM语句中的操作符。因此,当逻辑处理到达WHERE子句时,属于相应JOIN运算符的所有ON子句在逻辑上“已经发生”。这意味着在LEFT JOIN的情况下,例如,在应用WHERE子句时,外部连接的语义已经发生。

我已经在这篇博文中更深入地解释了下面的例子。运行此查询时:

SELECT a.actor_id, a.first_name, a.last_name, count(fa.film_id)
FROM actor a
LEFT JOIN film_actor fa ON a.actor_id = fa.actor_id
WHERE film_id < 10
GROUP BY a.actor_id, a.first_name, a.last_name
ORDER BY count(fa.film_id) ASC;

LEFT JOIN实际上没有任何有用的效果,因为即使演员没有在电影中演出,演员也会被过滤,因为它的FILM_ID将为NULL, WHERE子句将过滤这样的一行。结果是这样的:

ACTOR_ID  FIRST_NAME  LAST_NAME  COUNT
--------------------------------------
194       MERYL       ALLEN      1
198       MARY        KEITEL     1
30        SANDRA      PECK       1
85        MINNIE      ZELLWEGER  1
123       JULIANNE    DENCH      1

也就是说,就好像我们内部连接了两个表。如果我们移动ON子句中的筛选谓词,它现在成为外部连接的标准:

SELECT a.actor_id, a.first_name, a.last_name, count(fa.film_id)
FROM actor a
LEFT JOIN film_actor fa ON a.actor_id = fa.actor_id
  AND film_id < 10
GROUP BY a.actor_id, a.first_name, a.last_name
ORDER BY count(fa.film_id) ASC;

这意味着结果将包含没有任何影片的演员,或者没有任何影片且FILM_ID < 10的演员

ACTOR_ID  FIRST_NAME  LAST_NAME     COUNT
-----------------------------------------
3         ED          CHASE         0
4         JENNIFER    DAVIS         0
5         JOHNNY      LOLLOBRIGIDA  0
6         BETTE       NICHOLSON     0
...
1         PENELOPE    GUINESS       1
200       THORA       TEMPLE        1
2         NICK        WAHLBERG      1
198       MARY        KEITEL        1

简而言之

总是把谓词放在逻辑上最有意义的地方。

在INNER join中,它们是可互换的,优化器会随意重新排列它们。

在OUTER连接上,它们不一定是可互换的,这取决于它们所依赖的连接的哪一侧。

我把它们放在任意一个位置,取决于可读性。

从字面上看,它们是等价的。

在大多数开源数据库(最著名的例子是MySql和postgresql)中,查询计划是出现在关系数据库管理系统中的访问路径选择(Selinger et al, 1979)中的经典算法的变体。在这种方法中,条件有两种类型

引用单个表的条件(用于筛选) 引用两个表的条件(不管它们出现在哪里,都被视为连接条件)

特别是在MySql中,通过跟踪优化器,您可以看到自己的join ..On条件在解析过程中被等价的where条件替换。类似的事情也发生在postgresql中(虽然没有办法通过日志看到它,你必须阅读源代码描述)。

无论如何,主要的一点是,这两种语法变体之间的差异在解析/查询-重写阶段就消失了,甚至没有达到查询计划和执行阶段。因此,就性能而言,它们是否相同是毫无疑问的,它们在进入执行阶段之前就已经完全相同了。

你可以用explain来验证他们的计划是否相同。例如,在postgres中,计划将包含一个join子句,即使你没有在语法中使用join..on。

Oracle和SQL server不是开源的,但是,据我所知,它们基于等价规则(类似于关系代数中的规则),并且它们在这两种情况下也产生相同的执行计划。

显然,对于外部连接,这两种语法风格是不相等的,对于那些你必须使用join…在语法

它们不是一回事。

考虑一下这些问题:

SELECT *
FROM Orders
LEFT JOIN OrderLines ON OrderLines.OrderID=Orders.ID
WHERE Orders.ID = 12345

and

SELECT *
FROM Orders
LEFT JOIN OrderLines ON OrderLines.OrderID=Orders.ID 
    AND Orders.ID = 12345

第一个将返回订单及其行(如果有的话),订单号为12345。 第二个将返回所有订单,但只有订单12345有与之关联的行。

使用INNER JOIN,这两个子句实际上是等效的。然而,仅仅因为它们在功能上相同,产生相同的结果,并不意味着这两种子句具有相同的语义。

我的做法是:

Always put the join conditions in the ON clause if you are doing an INNER JOIN. So, do not add any WHERE conditions to the ON clause, put them in the WHERE clause. If you are doing a LEFT JOIN, add any WHERE conditions to the ON clause for the table in the right side of the join. This is a must, because adding a WHERE clause that references the right side of the join will convert the join to an INNER JOIN. The exception is when you are looking for the records that are not in a particular table. You would add the reference to a unique identifier (that is not ever NULL) in the RIGHT JOIN table to the WHERE clause this way: WHERE t2.idfield IS NULL. So, the only time you should reference a table on the right side of the join is to find those records which are not in the table.