使用JOIN时WHERE Clause vs ON

时间:2012-04-24 11:48:49

标签: 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);

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

5 个答案:

答案 0 :(得分:43)

请注意与外连接的区别。在b.IsApproved的{​​{1}}条件中添加ON过滤器(在右侧表格栏上)的查询:

JOIN

NOT 与将过滤器放在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没有b.BarId),因此f.BarIdb.IsApproved对于所有此类失败的连接行,这些行将被过滤掉。

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

<强>更新: 要完成Conrad提出的问题,可选过滤器的等效LOJ将是:

LEFT JOIN

即。 (b.BarId = f.BarId)子句需要同时考虑连接失败SELECT * FROM Foo f LEFT OUTER JOIN Bar b ON (b.BarId = f.BarId) WHERE (b.IsApproved IS NULL OR b.IsApproved = 1); 和过滤器是否被忽略的条件,以及连接成功和必须应用过滤器的条件。 (WHERE(NULL)可以针对b.IsApproved

进行测试

我添加了SqlFiddle together here,用于说明b.BarId过滤器相对于NULL的各种展示位置之间的差异。

答案 1 :(得分:29)

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

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


尽管如此,您应该将所有连接连接放在ON子句以及WHERE子句的所有限制上。

答案 2 :(得分:6)

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

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

答案 3 :(得分:0)

我似乎有些情况下优化器即使在最新版本的MSSQL上也不够智能 - 而且性能差异很大。

但这是一个例外,大部分时间SQL Server优化器都会解决问题并获得正确的计划。

因此保留了在WHERE子句上使用过滤器并在需要时进行优化的策略。

答案 4 :(得分:0)

我刚刚针对四个表运行了一个查询测试 - 一个主表有三个INNER JOIN和总共四个参数,并比较了两种方法的执行计划(使用JOIN的ON中的过滤条件,以及然后也在WHERE子句中。

执行计划完全相同。我在SQL Server 2008 R2上运行它。