假设我有以下 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);
这可能不是这里最好的案例示例,但是这两者之间有什么性能差异吗?
请注意与外部连接的区别。一个查询,其中b.IsApproved
(在右表,Bar)的过滤器被添加到ON
条件中JOIN
:
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);
由于 for 'failed' 外连接Bar
(即没有b.BarId
for a f.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)
到LEFT OUTER JOIN
on 条件的效果是在(b.IsApproved = 1)
为 false 时将任何正确的表列清空,即按照通常应用于LEFT JOIN
条件 on的相同规则(b.BarId = f.BarId)
。
更新:要完成康拉德提出的问题,可选过滤器的等效 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.IsApproved
或b.BarId
可以测试NULL
)
我在这里放了一个 SqlFiddle,b.IsApproved
它展示了过滤器的不同位置相对于JOIN
.
不,查询优化器足够聪明,可以为两个示例选择相同的执行计划。
您可以使用SHOWPLAN
来检查执行计划。
尽管如此,您应该将所有连接连接放在ON
子句上,并将所有限制放在WHERE
子句上。
SELECT * FROM Foo f
INNER JOIN Bar b ON b.BarId = f.BarId
WHERE b.IsApproved = 1;
这是更好的形式。它易于阅读和修改。在商业世界中,这就是你想要的。就性能而言,它们是相同的。
我似乎在某些情况下,即使在最新版本的 MSSQL 上优化器也不够聪明——而且性能差异是巨大的。
但这是一个例外,大多数时候 SQL Server 优化器会解决问题并获得正确的计划。
所以保持在 WHERE 子句上使用过滤器的策略,并在需要时进行优化。
我刚刚对四个表进行了查询测试——一个主表具有三个 INNER JOIN 和总共四个参数,并比较了两种方法的执行计划(使用 JOIN 的 ON 中的过滤条件,然后也在WHERE 子句)。
执行计划完全相同。我在 SQL Server 2008 R2 上运行了它。