假设我有以下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);
这可能不是这里最好的案例样本,但这两者之间是否有任何性能差异?
答案 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.BarId
为b.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上运行它。