查询是
SELECT SUM(Amount) AS SummaryTotal
FROM PDetail WITH(NOLOCK)
WHERE ClientID = @merchid
AND PostedDate BETWEEN @datebegin AND @dateend
该表包含103,129,000行。
快速计划通过ClientId在日期上使用残差谓词进行查找,但需要进行96次查找来检索Amount
。<ParameterList>
计划中的部分如下。
<ParameterList>
<ColumnReference Column="@dateend"
ParameterRuntimeValue="'2013-02-01 23:59:00.000'" />
<ColumnReference Column="@datebegin"
ParameterRuntimeValue="'2013-01-01 00:00:00.000'" />
<ColumnReference Column="@merchid"
ParameterRuntimeValue="(78155)" />
</ParameterList>
慢速计划按日期查找,并具有查找以评估ClientId上的剩余谓词并检索金额的功能(估计1与实际7,388,383)。该<ParameterList>
部分是
<ParameterList>
<ColumnReference Column="@EndDate"
ParameterCompiledValue="'2013-02-01 23:59:00.000'"
ParameterRuntimeValue="'2013-02-01 23:59:00.000'" />
<ColumnReference Column="@BeginDate"
ParameterCompiledValue="'2013-01-01 00:00:00.000'"
ParameterRuntimeValue="'2013-01-01 00:00:00.000'" />
<ColumnReference Column="@ClientID"
ParameterCompiledValue="(78155)"
ParameterRuntimeValue="(78155)" />
</ParameterList>
在第二种情况下,ParameterCompiledValue
它不为空。SQL Server成功嗅探查询中使用的值。
《 SQL Server 2005实用故障排除》这本书说的是关于使用局部变量的信息
使用局部变量来消除参数嗅探是一种很常见的技巧,但是OPTION (RECOMPILE)
和OPTION (OPTIMIZE FOR)
提示...通常是更优雅,风险更低的解决方案
注意
在SQL Server 2005中,语句级编译允许将存储过程中的单个语句的编译推迟到第一次执行查询之前进行。届时将知道局部变量的值。从理论上讲,SQL Server可以利用此功能来嗅探局部变量值,就像嗅探参数一样。但是,由于在SQL Server 7.0和SQL Server 2000+中通常使用局部变量来消除参数嗅探,因此在SQL Server 2005中未启用对局部变量的嗅探。在以后的SQL Server版本中可以启用局部变量嗅探,尽管这样做很好。如果可以选择,请使用本章概述的其他选项之一。
经过快速测试,上述行为在2008年和2012年仍然相同,并且仅在使用显式OPTION RECOMPILE
提示时,才嗅探变量以进行延迟编译。
DECLARE @N INT = 0
CREATE TABLE #T ( I INT );
/*Reference to #T means this statement is subject to deferred compile*/
SELECT *
FROM master..spt_values
WHERE number = @N
AND EXISTS(SELECT COUNT(*) FROM #T)
SELECT *
FROM master..spt_values
WHERE number = @N
OPTION (RECOMPILE)
DROP TABLE #T
尽管推迟了编译,但该变量未被嗅探,并且估计的行数不准确
因此,我认为慢速计划与查询的参数化版本有关。
的ParameterCompiledValue
是等于ParameterRuntimeValue
对所有的参数,所以这是不典型参数嗅探(其中计划被编译为一个组值然后另一组值运行)。
问题在于为正确的参数值编译的计划是不合适的。
您可能会按此处和此处所述的日期递增来解决问题。对于具有1亿行的表,您需要在SQL Server自动为您更新统计信息之前插入(或修改)2000万。似乎上次更新它们时,零行与查询中的日期范围匹配,但现在有700万行。
您可以安排更频繁的统计信息更新,考虑2389 - 90
使用跟踪标志或使用OPTIMIZE FOR UKNOWN
它,以便仅依靠猜测而不是能够使用该datetime
列上当前具有误导性的统计信息。
在下一版本的SQL Server(2012年之后)中,可能不需要这样做。一个相关的“连接”项包含有趣的响应
由Microsoft发布于2012年8月28日,下午1:35。
我们已经完成了下一个主要版本的基数估计增强,从本质上解决了这一问题。预览发布后,请继续关注细节。埃里克
本杰明·内瓦雷斯(Benjamin Nevarez)在文章末尾着眼于2014年的改进:
初探新的SQL Server基数估计器。
在这种情况下,新的基数估计值似乎会回落并使用平均密度,而不是给出1行估计值。
有关2014基数估计器和此处的升序关键问题的其他一些详细信息:
SQL Server 2014中的新功能–第2部分–新基数估计