SQL Server 2008:为什么在首先满足另一个逻辑条件时进行表格扫描?

时间:2010-09-17 14:03:47

标签: sql-server tsql sql-server-2008 sql-execution-plan

请考虑以下代码:

declare @var bit = 0

select * from tableA as A
where
1=
(case when @var = 0 then 1
      when exists(select null from tableB as B where A.id=B.id) 
      then 1
      else 0
end)

由于变量@var设置为0,因此评估搜索案例运算符的结果为1.在案例文档中,写入它直到第一个WHEN为TRUE时才进行求值。但是当我查看执行计划时,我也看到了tableB也被扫描了。

有人知道为什么会这样吗?当另一个逻辑条件被评估为TRUE时,可能有一些方法可以避免第二个表扫描?

2 个答案:

答案 0 :(得分:3)

因为编译和缓存的计划需要适用于@var

的所有可能值

您需要使用类似

的内容
if (@var = 0)
select * from tableA 
else
select * from tableA as A
where exists(select * from tableB as B where A.id=B.id) 

即使OPTION RECOMPILE看起来也不会有帮助。它仍然没有为您提供文字0=0

的计划
declare @var bit = 0

select * from 
master.dbo.spt_values  as A
where
1=
(case when 0 = @var then 1
      when exists(select null from master.dbo.spt_values as B where A.number=B.number) 
      then 1
      else 0
end)
option(recompile)

Plan http://img189.imageshack.us/img189/3977/executionplan.jpg

select * from 
master.dbo.spt_values  as A
where
1=
(case when 0 = 0 then 1
      when exists(select null from master.dbo.spt_values as B where A.number=B.number) 
      then 1
      else 0
end)

Plan http://img193.imageshack.us/img193/3977/executionplan.jpg

RE:评论中的问题。在启用“包括实际执行计划”选项的情况下尝试以下操作。

declare @var bit = datepart(second,GETDATE())%2

print @var

if (@var = 0)
select * from 
master.dbo.spt_values  --8BA71BA5-3025-4967-A0C8-38B9FBEF8BAD
else
select * from 
master.dbo.spt_values  as A --8BA71BA5-3025-4967-A0C8-38B9FBEF8BAD
where exists(select null from master.dbo.spt_values as B where A.number=B.number) 

然后尝试

SELECT usecounts, cacheobjtype, objtype, text, query_plan
FROM sys.dm_exec_cached_plans 
CROSS APPLY sys.dm_exec_sql_text(plan_handle) 
CROSS APPLY sys.dm_exec_query_plan(plan_handle) 
where text like '%8BA71BA5-3025-4967-A0C8-38B9FBEF8BAD%'

编译计划看起来像

Plan http://img178.imageshack.us/img178/3977/executionplan.jpg

实际执行计划将仅显示实际执行的一条路径。

答案 1 :(得分:1)

如果tableB的行数很少,则表扫描是最快的方法。

动态搜索条件的最佳来源:

Dynamic Search Conditions in T-SQL by Erland Sommarskog

如果可以使用索引,对于如何执行此操作会有很多微妙的含义。如果您使用的是SQL Server 2008的正确版本,则只需将OPTION (RECOMPILE)添加到查询中,并将运行时的局部变量值用于优化。

考虑到这一点,OPTION (RECOMPILE)将采用此代码(其中没有索引可用于此OR的混乱):

WHERE
    (@search1 IS NULL or Column1=@Search1)
    AND (@search2 IS NULL or Column2=@Search2)
    AND (@search3 IS NULL or Column3=@Search3)

并在运行时优化它(假设只有@ Search2传入了一个值):

WHERE
    Column2=@Search2

并且可以使用索引(如果在Column2上定义了一个索引)