Sql 使用JOIN时WHERE子句vs ON
假设我有以下T-SQL代码:Sql 使用JOIN时WHERE子句vs ON,sql,sql-server,tsql,sql-server-2008-r2,inner-join,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); 这可能不是这里的最佳示例,但这两个示例之间是否存在性能差异?否,查询优化器足够聪明,可以为这两个示例选择
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);
这可能不是这里的最佳示例,但这两个示例之间是否存在性能差异?否,查询优化器足够聪明,可以为这两个示例选择相同的执行计划 您可以使用
SHOWPLAN
检查执行计划
但是,您应该将所有连接放在
on
子句上,并将所有限制放在WHERE
子句上
SELECT * FROM Foo f
INNER JOIN Bar b ON b.BarId = f.BarId
WHERE b.IsApproved = 1;
这是最好的方式。它易于阅读和修改。在商界,这是你想要的。但就性能而言,它们是相同的。只需注意外部联接的差异。一个查询,其中
b.IsApproved
(在右侧表格的条形图上)的过滤器被添加到连接的on
条件中:
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
的“失败”外部联接(即,对于f.BarId
没有b.BarId
),这将使所有此类失败联接行的b.IsApproved
保持为NULL
,然后将过滤掉这些行
另一种方法是,对于第一个查询,LEFT-OUTER-JOIN Bar b ON(b.IsApproved=1)和(b.BarId=f.BarId)
将始终返回左表行,因为LEFT-OUTER-JOIN
保证即使连接失败也会返回左表行。但是,当(b.IsApproved=1)
为false时,将(b.IsApproved=1)
添加到左外部联接
on条件的效果是将任何右表列清空,即按照通常应用于(b.BarId=f.BarId)
上的左联接
条件的相同规则
更新:
为了完成Conrad提出的问题,可选过滤器的等效LOJ为:
SELECT *
FROM Foo f
LEFT OUTER JOIN Bar b ON (b.BarId = f.BarId)
WHERE (b.IsApproved IS NULL OR b.IsApproved = 1);
即<<代码> 子句需要考虑连接是否失败>代码>(NULL)< /代码>和忽略过滤器的情况,以及连接成功的地方和必须应用的筛选器。(
b.IsApproved
或b.BarId
可以测试NULL
)
我放了一个例子来说明b的不同位置之间的差异。相对于JOIN
我发现在某些情况下,即使在最新版本的MSSQL上,优化器也不够智能,性能差异很大
但这是一个例外,大多数情况下SQLServer优化器将解决问题并获得正确的计划
因此,请遵守在WHERE子句上使用过滤器的策略,并在需要时进行优化。我刚刚对四个表(一个主表有三个内部联接,总共有四个参数)执行了一个查询测试,并比较了两种方法的执行计划(在联接的on中使用过滤器条件,然后在WHERE子句中使用过滤器条件)
执行计划完全相同。我在SQL Server 2008 R2上运行了此功能。比我快。虽然出于偏好,我还是选择加入,因为它更具描述性。谢谢!想象一个有7或8个内部联接的情况。您的答案也适用于这些情况吗?@Ste IMO,将所有内容都放在JOIN
中实际上更令人困惑。使用JOIN
关联查询中的表。使用WHERE
过滤结果。只有当你混合使用这两种方法,并且只使用其中一种时,查询才会变得难以阅读。@恶心。Fair point和我都同意混合变得不可管理。@Ste:实际上我通常更喜欢JOIN的混合,只要在查询编写中强制执行每个关键字的用途。JOIN子句确定如何将表链接到“宽”结果集中,然后WHERE子句确定对所述结果的过滤。有鉴于此,我觉得用连接和WHERE来破译查询比只使用连接来破译查询更容易,就像破译连接比只使用WHERE子句来定义连接和筛选条件更容易一样。这里有一个类似的问题:机器会找到它并适当地优化它。但是,对于几年后需要调试\修改\支持您的代码的人员,请将过滤条件保留在WHERE
中,并将连接条件保留在ON
@KM中。我并不总是知道如何区分连接条件和过滤器。例如,我认为在连接中更好,那么这是一个“连接条件”吗?我甚至不知道如何重写等价的where子句。连接条件是:tableA.column=tableB.column
过滤条件是tableA.column=5
。在执行外部联接(左/右)时,必须以这种方式将筛选条件放在
上的中,或将你的编码到WHERE
(tableA.Column=5或tableA.Column为NULL)
在我目前的情况下,我赞成WHERE子句,但无法避免怀疑是否存在性能差异。谢谢!很好。如果将来自外部联接的筛选条件测试数据放入外部联接本身,那么将得到比预期更多的行,因为无论Bar的状态或存在与否,都将返回所有的FOO。如果将筛选与联接分开指定,则首先联接两个表中的行,然后筛选器从不符合条件的表中删除整行。@NONB Ok,但如果将第二次查询中的where子句更正为,其中b.IsApproved=1或b.BarId为Null
,则相同。现在您要做哪一个?@nonnn-um您不需要或(b.BarId为NULL)
在左侧连接版本中,仅在WHERE版本中使用,并且您希望使其相同。