Sql 使用JOIN时WHERE子句vs ON

Sql 使用JOIN时WHERE子句vs ON,sql,sql-server,tsql,sql-server-2008-r2,inner-join,Sql,Sql Server,Tsql,Sql Server 2008 R2,Inner Join,假设我有以下T-SQL代码: SELECT * FROM Foo f INNER JOIN Bar b ON b.BarId = f.BarId; WHERE b.IsApproved = 1; 以下命令还返回相同的行集: SELECT * FROM Foo f INNER JOIN Bar b ON (b.IsApproved = 1) AND (b.BarId = f.BarId); 这可能不是这里的最佳示例,但这两个示例之间是否存在性能差异?否,查询优化器足够聪明,可以为这两个示例选择

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

SELECT * FROM Foo f
INNER JOIN Bar b ON b.BarId = f.BarId;
WHERE b.IsApproved = 1;
以下命令还返回相同的行集:

SELECT * FROM Foo f
INNER JOIN Bar b ON (b.IsApproved = 1) AND (b.BarId = f.BarId);

这可能不是这里的最佳示例,但这两个示例之间是否存在性能差异?

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

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


但是,您应该将所有连接放在
on
子句上,并将所有限制放在
WHERE
子句上

SELECT * FROM Foo f
INNER JOIN Bar b ON b.BarId = f.BarId
WHERE b.IsApproved = 1;

这是最好的方式。它易于阅读和修改。在商界,这是你想要的。但就性能而言,它们是相同的。

只需注意外部联接的差异。一个查询,其中
b.IsApproved
(在右侧表格的条形图上)的过滤器被添加到
连接的
on
条件中:

SELECT * 
FROM Foo f 
LEFT OUTER JOIN Bar b ON (b.IsApproved = 1) AND (b.BarId = f.BarId); 
与在
WHERE
子句中放置过滤器不同:

SELECT * 
FROM Foo f 
LEFT OUTER JOIN Bar b ON (b.BarId = f.BarId)
WHERE (b.IsApproved = 1); 
由于对于
Bar
的“失败”外部联接(即,对于
f.BarId
没有
b.BarId
),这将使所有此类失败联接行的
b.IsApproved
保持为
NULL
,然后将过滤掉这些行

另一种方法是,对于第一个查询,
LEFT-OUTER-JOIN Bar b ON(b.IsApproved=1)和(b.BarId=f.BarId)
将始终返回左表行,因为
LEFT-OUTER-JOIN
保证即使连接失败也会返回左表行。但是,当
(b.IsApproved=1)
为false时,将
(b.IsApproved=1)
添加到
左外部联接
on条件的效果是将任何右表列清空,即按照通常应用于
(b.BarId=f.BarId)
上的
左联接
条件的相同规则

更新: 为了完成Conrad提出的问题,可选过滤器的等效LOJ为:

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

即<<代码> 子句需要考虑连接是否失败>代码>(NULL)< /代码>和忽略过滤器的情况,以及连接成功的地方和必须应用的筛选器。(

b.IsApproved
b.BarId
可以测试
NULL


我放了一个例子来说明
b的不同位置之间的差异。相对于
JOIN

我发现在某些情况下,即使在最新版本的MSSQL上,优化器也不够智能,性能差异很大

但这是一个例外,大多数情况下SQLServer优化器将解决问题并获得正确的计划


因此,请遵守在WHERE子句上使用过滤器的策略,并在需要时进行优化。

我刚刚对四个表(一个主表有三个内部联接,总共有四个参数)执行了一个查询测试,并比较了两种方法的执行计划(在联接的on中使用过滤器条件,然后在WHERE子句中使用过滤器条件)


执行计划完全相同。我在SQL Server 2008 R2上运行了此功能。

比我快。虽然出于偏好,我还是选择加入,因为它更具描述性。谢谢!想象一个有7或8个内部联接的情况。您的答案也适用于这些情况吗?@Ste IMO,将所有内容都放在
JOIN
中实际上更令人困惑。使用
JOIN
关联查询中的表。使用
WHERE
过滤结果。只有当你混合使用这两种方法,并且只使用其中一种时,查询才会变得难以阅读。@恶心。Fair point和我都同意混合变得不可管理。@Ste:实际上我通常更喜欢JOIN的混合,只要在查询编写中强制执行每个关键字的用途。JOIN子句确定如何将表链接到“宽”结果集中,然后WHERE子句确定对所述结果的过滤。有鉴于此,我觉得用连接和WHERE来破译查询比只使用连接来破译查询更容易,就像破译连接比只使用WHERE子句来定义连接和筛选条件更容易一样。这里有一个类似的问题:机器会找到它并适当地优化它。但是,对于几年后需要调试\修改\支持您的代码的人员,请将过滤条件保留在
WHERE
中,并将连接条件保留在
ON
@KM中。我并不总是知道如何区分连接条件和过滤器。例如,我认为在连接中更好,那么这是一个“连接条件”吗?我甚至不知道如何重写等价的where子句。连接条件是:
tableA.column=tableB.column
过滤条件是
tableA.column=5
。在执行外部联接(左/右)时,必须以这种方式将筛选条件放在
上的
中,或将你的
编码到WHERE
(tableA.Column=5或tableA.Column为NULL)
在我目前的情况下,我赞成WHERE子句,但无法避免怀疑是否存在性能差异。谢谢!很好。如果将来自外部联接的筛选条件测试数据放入外部联接本身,那么将得到比预期更多的行,因为无论Bar的状态或存在与否,都将返回所有的FOO。如果将筛选与联接分开指定,则首先联接两个表中的行,然后筛选器从不符合条件的表中删除整行。@NONB Ok,但如果将第二次查询中的where子句更正为
,其中b.IsApproved=1或b.BarId为Null
,则相同。现在您要做哪一个?@nonnn-um您不需要
或(b.BarId为NULL)
在左侧连接版本中,仅在WHERE版本中使用,并且您希望使其相同。