使用JOIN时WHERE Clause vs ON

tug*_*erk 41 sql t-sql sql-server inner-join sql-server-2008-r2

假设我有以下T-SQL代码:

SELECT * FROM Foo f
INNER JOIN Bar b ON b.BarId = f.BarId;
WHERE b.IsApproved = 1;
Run Code Online (Sandbox Code Playgroud)

以下一行也返回相同的行集:

SELECT * FROM Foo f
INNER JOIN Bar b ON (b.IsApproved = 1) AND (b.BarId = f.BarId);
Run Code Online (Sandbox Code Playgroud)

这可能不是这里最好的案例样本,但这两者之间是否有任何性能差异?

Stu*_*tLC 43

请注意与外连接的区别.将b.IsApproved(在右表,Bar上)的过滤器添加到以下ON条件的查询JOIN:

SELECT * 
FROM Foo f 
LEFT OUTER JOIN Bar b ON (b.IsApproved = 1) AND (b.BarId = f.BarId); 
Run Code Online (Sandbox Code Playgroud)

一样放置过滤器的WHERE子句:

SELECT * 
FROM Foo f 
LEFT OUTER JOIN Bar b ON (b.BarId = f.BarId)
WHERE (b.IsApproved = 1); 
Run Code Online (Sandbox Code Playgroud)

由于对"失败"外连接到Bar(即不存在b.BarIdf.BarId),这将离开b.IsApprovedNULL是所有这些失败的加入行,然后将这些行会被过滤掉.

另一种看待这种情况的方法是,对于第一个查询,LEFT OUTER JOIN Bar b ON (b.IsApproved = 1) AND (b.BarId = f.BarId)将始终返回LEFT表行,因为LEFT OUTER JOIN即使连接失败,也会保证返回LEFT表行.但是,添加(b.IsApproved = 1)LEFT OUTER JOINon条件的效果是在(b.IsApproved = 1)false为false 时将任何右表列清空,即按照通常应用于LEFT JOIN条件的相同规则(b.BarId = f.BarId).

更新:要完成康拉德提出的问题,可选过滤器的等效LOJ将是:

SELECT * 
FROM Foo f 
LEFT OUTER JOIN Bar b ON (b.BarId = f.BarId)
WHERE (b.IsApproved IS NULL OR b.IsApproved = 1);
Run Code Online (Sandbox Code Playgroud)

ie该WHERE子句需要考虑连接失败(NULL)和过滤器是否被忽略的条件,以及连接成功和必须应用过滤器的条件.(b.IsApprovedb.BarId可以测试NULL)

我在这里放了一个SqlFiddle,它演示了b.IsApproved过滤器相对于过滤器的各个位置之间的差异JOIN.

  • @nonnn嗯,你只需要在WHERE版本的左连接版本中不需要`OR(b.BarId IS NULL)`,你想让它变得相同. (2认同)

aF.*_*aF. 30

不,查询优化器足够聪明,可以为两个示例选择相同的执行计划.

您可以使用SHOWPLAN检查执行计划.


但是,您应该将所有连接连接放在ON子句上以及对子句的所有限制WHERE.

  • @Ste IMO,把所有东西放在`JOIN`中实际上更令人困惑.使用`JOIN`与查询中的表相关联.使用`WHERE`来过滤结果.当你将两者混合使用时,**只使用一个或另一个,查询变得难以阅读. (14认同)
  • 打败我.虽然作为一个优先选择,我会选择JOIN,因为它更具描述性. (2认同)
  • 谢谢!想象一下有 7 或 8 个 INNER JOINS 的情况。您的回答是否也适用于这些情况? (2认同)
  • @Ste:实际上,我通常更喜欢 JOIN 和 WHERE 的组合,前提是每个关键字的用途在查询编写中得到强制执行。JOIN 子句确定如何将表链接到“宽”结果集,然后 WHERE 子句确定对所述结果的过滤。鉴于此,我觉得用 JOIN 和 WHERE 破译查询比仅用 JOIN 破译查询更容易,就像破译 JOIN 比只用 WHERE 子句定义连接和过滤条件的查询更容易。 (2认同)

Lan*_*ens 6

SELECT * FROM Foo f
INNER JOIN Bar b ON b.BarId = f.BarId
WHERE b.IsApproved = 1;
Run Code Online (Sandbox Code Playgroud)

这是更好的形式.它易于阅读,易于修改.在商业世界中,这是您想要的.就性能而言,它们是相同的.