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

Fra*_*sCN 5 t-sql sql-server 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)
Run Code Online (Sandbox Code Playgroud)

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

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

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

Mik*_*son 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
Run Code Online (Sandbox Code Playgroud)