2012-04-24 130 views
39

假设我有以下的T-SQL代码时:WHERE子句VS使用JOIN

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); 

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

+2

这里有一个类似的问题:http://stackoverflow.com/questions/2509987/which-sql-query-is-faster-filter-on-join-criteria-or-where-clause – 2012-04-24 11:52:59

+11

机器会计算出来并对其进行适当优化。但是,对于需要调试\修改\支持代码的人来说,在“WHERE”中保留过滤条件并在“ON”中加入条件。 – 2012-04-24 11:58:42

+0

@KM。我并不总是知道如何区分什么是连接条件和什么是过滤器。例如[在这个答案](http://stackoverflow.com/a/9303069/119477)我认为它更好的加入,所以是“加入条件”呢? [这是另一个例子](http://stackoverflow.com/a/6473403/119477),我甚至不知道如何重写等价的where子句。 – 2012-04-24 17:55:31

回答

28

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

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


不过,你应该把所有加入的ON从句连接,并在WHERE条款的所有限制。

+2

击败我。虽然作为一个偏好问题,但我会使用JOIN,因为它更具描述性。 – Ste 2012-04-24 11:52:57

+1

谢谢!想象一下有7或8个INNER JOIN的情况。你的答案也适用于这些情况吗? – tugberk 2012-04-24 11:52:59

+13

@Ste IMO,将所有内容放在'JOIN'中实际上更令人困惑。使用'JOIN'来关联查询中的表格。使用'WHERE'来过滤结果。这是当你混合使用**和**时,只有一个或另一个查询变得难以阅读。 – Yuck 2012-04-24 11:56:17

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

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

+0

在我目前的情况下,我倾向于WHERE子句,但无法避免想知道是否存在perf差异。谢谢! – tugberk 2012-04-24 11:54:17

42

只要注意与外连接的区别。其中b.IsApproved过滤器(上右表,酒吧)被添加到JOINON条件查询:

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.BarIdf.BarId),这将离开b.IsApproved作为NULL所有这样的发加入连接行,然后这些行将被过滤掉。

查看此问题的另一种方法是,对于第一个查询,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)的作用是当(b.IsApproved = 1)为假时(即按照通常应用于(b.BarId = f.BarId)上的LEFT JOIN条件的相同规则)将任何右表列中的NULL排除。

更新: 要完成康拉德提出的问题,一个可选的过滤器相当于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)和过滤器将被忽略,并且连接成功并且必须应用过滤器。(b.IsApprovedb.BarId可以为NULL被测试)

我已经把一个SqlFiddle together here这表明相对于JOINb.IsApproved滤波器的各种放置之间的差异。

+0

+1,好点! – 2012-04-24 12:03:35

+0

非常好的一点。如果将外部联接的过滤标准测试数据放入外部联接本身,您将获得比您预期更多的行,因为无论Bar的状态或存在如何,都会返回所有Foos。当过滤与连接分开指定时,两个表中的行首先连接,然后过滤器从不符合条件的表中移除整个行。 – KeithS 2012-04-24 15:11:56

+1

@nonnb好吧,但如果您更正了第二个查询中的WHERE子句到'WHERE b。IsApproved = 1或b.BarId是Null'它是一样的。你现在做哪一个? – 2012-04-24 16:51:08

0

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

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

因此,在WHERE子句中使用过滤器并在需要时进行优化。

0

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

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