sql – “Between”运算符在使用参数时生成错误的查询计划

我有一个简单的日期表(日期,日期ID),其中包含1900年1月1日至2100年12月31日之间的日期列表.

当使用between运算符和硬编码参数值从表中进行选择时,我得到一个正确的查询计划,其中3个估计行与2个实际行相比:

select v.Date from Dates v
where v.Date between '20130128' and '20130129';

但是,当使用参数替换硬编码值时,查询计划会更改为非常糟糕的计划,估计行数超过6000,实际行数仅为2行:

select v.Date from Dates v
where v.Date between @startdate and @enddate;

查询计划本身是相同的,只是估计行的差异导致参数化查询比硬编码查询慢大约4倍.有什么我不知道为什么参数化版本运行得那么慢,我可以给SQL Server什么索引/提示,以帮助它使用正确的查询计划?

一些额外的信息:

>使用简单的等于=标准时不会出现问题,它似乎特定于运算符之间.
>如果我在参数化查询的末尾添加选项(重新编译),我会得到一个完美的查询计划,与硬编码查询相同.
>日期表只有两列,Date和DateID,主键DateID列上有聚簇索引,Date列上有唯一的非聚簇索引.都有更新的统计数据.
>查询计划为硬编码查询执行自动参数化,将硬编码值替换为@ 1和@2,并将查询显示为大写.它似乎不会对参数化查询执行任何转换.
>使用SQL Server 2008 R2.

我知道足够让人怀疑这是某种参数嗅探问题.为什么不在查询中添加选项(重新编译)?这被用作更大的复杂查询的一部分,我理解良好的做法是让SQL Server尽可能地从缓存中重用查询计划.

编辑和更新:感谢目前为止的深思熟虑的回应.为了进一步细化问题,查询计划为上述两个查询使用了一个非常好的索引,但为什么它不能识别参数化查询的日期范围只有两天宽,为什么它认为范围是6000行宽?特别是当查看查询计划时,SQL Server正在为硬编码查询执行自动参数化?在基础查询计划中,两个计划看起来都相同,因为它们都是参数化的!

最佳答案
首次运行查询时,查询计划基于参数值.这称为parameter sniffing.当您添加选项(重新编译)时,将为每次执行生成一个新计划.

查询计划基于SQL查询的哈希进行缓存.因此,对于两个版本的查询,都有一个不同的缓存槽.

添加选项(重新编译)是一个很好的解决方案.你也可以使用:

option (optimize for (@startdate = '20130128', @enddate = '20130129'));

生成查询计划,就像传入了这些值一样.

要进行测试,您可以使用以下命令从缓存中删除所有计划:

DBCC FREEPROCCACHE

转载注明原文:sql – “Between”运算符在使用参数时生成错误的查询计划 - 代码日志