2013-02-13 75 views
4

我有一个简单的日期表(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正在为硬编码查询执行自动参数化?在底层查询计划中,两个计划看起来都是相同的,因为它们都是参数化的!

+0

将'vdate在@startdate和@ enddate'之间替换为'v.Date> = @startdate AND v.Date <= @ enddate'时会发生什么? – 2013-02-13 10:26:10

+0

由于丹尼尔 - 测试和语义相同,生成的查询计划中没有任何更改,仍然生成约6000估计行。 – Tamar 2013-02-13 10:30:30

回答

3

查询计划基于首次运行查询时的参数值。这叫做parameter sniffing。当您添加option (recompile)时,会为每次执行生成一个新计划。

查询计划基于SQL查询的散列进行高速缓存。所以这两个版本的查询都有不同的缓存槽。

添加option (recompile)是一个很好的解决方案。你也可以使用:

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

生成查询计划,如果这些值已在已经通过

为了进行测试,可以从缓存中删除了所有的计划。

DBCC FREEPROCCACHE 
+0

感谢Andomar,我宁愿避免'选择(重新编译)',因为这个查询正在用于一个更大的复杂查询中,并且不希望整个查询每次都要重新编译。 'DBCC FREEPROCCACHE'对生成的查询计划没有影响。 – Tamar 2013-02-13 11:19:54

+0

我曾尝试过针对您的建议进行优化,如果日期相隔一天,它会生成正确的查询计划,但如果日期范围较宽(如一个月),则不会生成正确的计划。 – Tamar 2013-02-13 11:31:26

+0

感谢@Andomar,标记为“DBCC FREEPROCCACHE”没有改进日期子查询的查询计划,当然,如果用作更大复杂查询的一部分,它确实可以提高查询计划的估计值,因此解决了问题。 – Tamar 2013-02-13 23:14:12

2

问题可能是由于parameter sniffing,这是一种基于您首次通过的参数优化查询计划的技术。

我在参数嗅探过去使用过日期参数时遇到了不好的经历 - 参数嗅探显然有一个机会,即最初为查询输入的值并不代表该查询的典型用法(导致查询计划针对非典型值进行了优化),但是使用日期参数特别是我经常发现生成的查询计划对于提供的所有值实际上效率非常低。我不知道为什么这是禁用参数嗅探通常修复它。

您可以通过使用OPTIMIZE FOR UNKNOWN(我从来没有使用过,因此不能保证它可以正常工作)防止SQL Server参数嗅探,或者将参数复制到本地变量中似乎可以防止SQL Server执行参数嗅探

-- Perform the query using @StartDateLocal and @EndDateLocal 
DECLARE @StartDateLocal DATETIME; 
SET @StartDateLocal = @StartDate; 

禁用参数以这种方式嗅探是不是每个相比,执行查询,除非查询是相当缓慢的成本编译查询计划通常是比较昂贵的时间迫使查询计划的重新编译更好。

+0

感谢贾斯汀,之前曾尝试过“优化未知”,但它对查询计划没有影响。我很高兴尝试使用局部变量,但在StackOverflow上阅读了很多关于这种做法的评论? – Tamar 2013-02-13 11:14:55

+0

@Tamar任何参考?我可以想象避免它的一个很好的理由是因为它阻止了参数嗅探,但是因为这里的意图可能不是问题:)我想不出其他原因来避免它,但它并不意味着没有一个。 – Justin 2013-02-13 11:33:11

+0

请参阅[此链接](http://stackoverflow.com/questions/14468603/does-assigning-stored-procedure-input-parameters-to-local-variables-help-optimiz)等等。一致认为,防止参数嗅探会有所帮助,但需要以一种不会在更大的查询中使用时不会破坏更广泛的计划优化的方式来实现。 – Tamar 2013-02-13 11:44:42

相关问题