我有一个简单的日期表(Date,DateID),其中包含1900年1月1日至2100年12月31日之间的日期列表。“Between”运算符在使用参数时生成错误的查询计划
使用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哪些索引/提示以帮助它使用正确的查询计划?
一些额外的信息:
- 使用简单的平等
=
标准时,就不会出现这个问题,似乎特定于between
运营商。 - 如果我将
option(recompile)
添加到参数化查询的末尾,我会得到一个完美的查询计划,与硬编码查询相同。 - 日期表只有两列Date和DateID,在主键DateID列上有聚簇索引,在Date列上有唯一的非聚簇索引。所有更新的统计数据。
- 查询计划为硬编码查询执行自动参数化,用@ 1和@ 2替换硬编码的值,并将查询显示为大写。它似乎没有为参数化查询执行任何转换。
- 使用SQL Server 2008 R2。
我知道足够
实现
嫌疑这是某种参数嗅探问题。为什么不将option(recompile)
添加到查询中?这被用作更大复杂查询的一部分,我理解的最佳做法是让SQL Server尽可能地从缓存中重新使用查询计划。
编辑和更新:感谢迄今为止的深思熟虑的回复。为了进一步细化问题,查询计划对上述两个查询都使用了一个非常好的索引,但为什么它没有认识到日期范围对于参数化查询只有两天宽,为什么它却认为范围是6000行宽?尤其是当查看查询计划时,SQL Server正在为硬编码查询执行自动参数化?在底层查询计划中,两个计划看起来都是相同的,因为它们都是参数化的!
将'vdate在@startdate和@ enddate'之间替换为'v.Date> = @startdate AND v.Date <= @ enddate'时会发生什么? – 2013-02-13 10:26:10
由于丹尼尔 - 测试和语义相同,生成的查询计划中没有任何更改,仍然生成约6000估计行。 – Tamar 2013-02-13 10:30:30