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语句的第一行使查询立即生效.
我已经读过,短路是不可能的,但有没有解决方法或构建可能更有效的查询的方法?
我们目前通过使用相同的SELECT/FROM/JOINS的不同版本但在WHERE子句中使用不同的参数集来解决这个问题,并且根据传递的参数,我们选择要经过的正确的select语句.这很长,很乱,很难维护.
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)