WHERE子句与ON时使用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); 

这可能不是最好的例子,但这两者之间有什么performance差异?

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

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


不过,你应该把ON子句的所有连接连接和WHERE子句的所有限制放在一起。

只要注意与外连接的区别。 在JOINON条件中添加b.IsApproved的filter(在右表Bar上)的查询:

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

与将filter放置在WHERE子句中不同:

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

因为对于'失败的'外连接到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) ,将(b.IsApproved = 1)添加到LEFT OUTER JOIN条件的效果是在(b.IsApproved = 1)为假时根据通常应用于LEFT JOIN条件(b.BarId = f.BarId)

更新 :要完成Conrad提出的问题,可选filter的等效LOJ将是:

 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)和filter是否被忽略的条件以及连接成功和filter必须被应用的情况。 ( b.IsApprovedb.BarId可以testingNULL

我已经把SqlFiddle放在一起,它演示了b.IsApprovedfilter相对于JOIN的不同位置之间的区别。

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

这是更好的forms去。 它易于阅读和易于修改。 在商业世界里,这是你想要的。 至于performance,他们是相同的。

我似乎有些情况下,即使在最新版本的MSSQL上,优化器也不够聪明 – 性能差异是怪物。

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

因此,保持在WHERE子句上使用filter的策略,并在需要时进行优化。

我只是对一个查询testing了四个表 – 一个主表有三个INNER JOIN和四个参数,并比较了两种方法的执行计划(在JOIN的ON中使用过滤条件,然后在WHERE子句)。

执行计划是完全一样的。 我在SQL Server 2008 R2上运行这个。