T-SQL - 使用非最优计划 - WHERE子句应该短路

时间:2013-04-06 13:32:14

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

我们有许多“搜索存储过程”,它们采用多个可空参数来搜索不同表中的数据行。它们通常是这样构建的:

SELECT      *
 FROM       Table1 T1
 INNER JOIN Table2 T2
     ON     T2.something = T1.something
 WHERE      (@parameter1 IS NULL OR T1.Column1 = @parameter1)
        AND (@parameter2 IS NULL OR T2.Column2 = @parameter2)
        AND (@parameter3 IS NULL OR T1.Column3 LIKE '%' + @parameter3 + '%')
        AND (@parameter4 IS NULL OR T2.Column4 LIKE '%' + @parameter4 + '%')       
        AND (@parameter5 IS NULL OR T1.Column5 = @parameter5)

这可以持续多达30-40个参数,我们注意到即使只提供了parameter1,执行计划也会通过其他表的索引扫描,这会显着减慢查询速度(几秒钟) 。测试向我们表明,只保留WHERE语句的第一行使查询立即生效。

  1. 我已经读过,短路是不可能的,但有没有解决方法或构建可能更有效的查询?

  2. 我们目前通过使用相同的SELECT / FROM / JOINS的不同版本但在WHERE子句中使用不同的参数集来解决这个问题,并且根据传递的参数,我们选择正确的select语句来完成。这很长,很乱,很难维护。

1 个答案:

答案 0 :(得分:4)

编译并存储SQL Server中的查询计划以供重用。即使SQL Server发现您的参数为null,它也必须提出一个查询计划,该计划适用于not null的情况。

SQL Server 2005中引入了查询提示option (recompile),但直到SQL Server 2008它才真正影响了您在此处的查询类型。

每次查询重新编译时,它都不会存储在查询计划缓存中,因此SQL Server可以自由地优化对null参数的检查。

在此处详细了解Dynamic Search Conditions in T-SQL

您可以测试一些示例代码,以查看查询计划中的差异。第一次调用SP将执行索引搜索,第二次调用将执行聚簇索引扫描。

create table T
(
  ID int identity primary key,
  Col1 int,
  Col2 int
);

go

create index IX_T on T(Col1);

go

create procedure GetT
  @Col1 int,
  @Col2 int
as

select ID
from T
where (Col1 = @Col1 or @Col1 is null) and
      (Col2 = @Col2 or @Col2 is null)
option (recompile);

go

exec GetT 1, null
exec GetT 1, 1