5

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

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. 我已经读到shortcuiting 是不可能的,但是是否有解决方法或方法来构建可能更有效的查询?

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

4

1 回答 1

4

SQL Server 中的查询计划经过编译和存储以供重复使用。即使 SQL Server 看到您的参数是null它,它也必须提出一个查询计划,该计划将适用于它们所在的情况not null

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

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

在此处阅读有关它的更多信息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
于 2013-04-06T15:26:41.110 回答