我遇到了一个奇怪的情况,在我的查询中附加OPTION (RECOMPILE)
导致它在半秒内运行,而省略它导致查询花费超过五分钟。
从查询分析器或通过SqlCommand.ExecuteReader()
从我的C#程序执行查询时就是这种情况。打电话(或不打电话)DBCC FREEPROCCACHE
或DBCC dropcleanbuffers
没有任何区别;查询结果总是随OPTION (RECOMPILE)
一起返回,没有它就会超过五分钟。始终使用相同的参数调用查询[为了此测试]。
我正在使用SQL Server 2008。
我对编写SQL非常熟悉,但之前从未在查询中使用过OPTION
命令,并且在扫描此论坛上的帖子之前不熟悉计划缓存的整个概念。我从帖子中了解到,OPTION (RECOMPILE)
是一项昂贵的操作。它显然为查询创建了一个新的查找策略。那么为什么然后,省略OPTION (RECOMPILE)
的后续查询是如此之慢?后续查询是否应该使用在前一次调用中计算的查找策略,其中包括重新编译提示?
拥有一个需要在每次调用时重新编译提示的查询是否非常不寻常?
对于入门级问题我很抱歉,但我无法真正做到这一点。
更新:我被要求发布查询...
select acctNo,min(date) earliestDate
from(
select acctNo,tradeDate as date
from datafeed_trans
where feedid=@feedID and feedDate=@feedDate
union
select acctNo,feedDate as date
from datafeed_money
where feedid=@feedID and feedDate=@feedDate
union
select acctNo,feedDate as date
from datafeed_jnl
where feedid=@feedID and feedDate=@feedDate
)t1
group by t1.acctNo
OPTION(RECOMPILE)
从查询分析器运行测试时,我预先添加以下行:
declare @feedID int
select @feedID=20
declare @feedDate datetime
select @feedDate='1/2/2009'
从我的C#程序调用它时,参数通过SqlCommand.Parameters
属性传入。
出于本讨论的目的,您可以假设参数永远不会改变,因此我们可以排除次优参数嗅觉作为原因。
有时使用OPTION(RECOMPILE)
是有道理的。根据我的经验,这是一个可行的选项,唯一一次是使用动态SQL。在您探究在您的情况下这是否有意义之前,我建议您重建统计数据。这可以通过运行以下命令来完成:
EXEC sp_updatestats
然后重新创建执行计划。这将确保在创建执行计划时,它将使用最新信息。
每次执行查询时,添加OPTION(RECOMPILE)
都会重建执行计划。我从来没有听说过被描述为creates a new lookup strategy
,但也许我们只是使用不同的术语来做同样的事情。
创建存储过程时(我怀疑您从.NET调用ad-hoc sql但是if you are using a parameterized query then this ends up being a stored proc call)SQL Server会根据数据库中的数据和传入的参数(parameter sniffing)尝试确定此查询的最有效执行计划。 ,然后缓存此计划。这意味着如果您创建查询,其中数据库中有10条记录,然后在有100,000,000条记录时执行它,则缓存的执行计划可能不再是最有效的。
总而言之 - 我认为OPTION(RECOMPILE)
在这里没有任何理由。我怀疑你只需要更新统计数据和执行计划。根据您的具体情况,重建统计数据可能是DBA工作的重要组成部分。如果您在更新统计数据后仍然遇到问题,我建议发布两个执行计划。
并回答你的问题 - 是的,我会说你每次执行查询时都要重新编译执行计划是非常不寻常的。
通常,当查询的运行与运行之间存在巨大差异时,我发现它通常是5个问题中的一个。
通常,当您编写查询时,您应该大致了解某些数据在表中的分布情况。例如,一列可以具有均匀分布的不同值的数量,或者它可以是倾斜的,80%的时间具有一组特定的值,无论分布是否会随着时间频繁变化或者是相当静态的。这将使您更好地了解如何构建有效的查询。但是,当调试查询性能有一个基础来建立一个假设,为什么它是缓慢或低效。
要添加OPTION(RECOMPILE)非常有用的优秀列表(由@CodeCowboyOrg给出),
在运行查询之前的第一个操作是碎片整理/重建索引和统计信息,这是您浪费时间的另一种方式。
你必须检查执行计划以确定它是否稳定(当你更改参数时是相同的),如果没有,你可能必须创建一个封面索引(在这种情况下为每个表)(知道系统你可以创建一个对于其他查询也很有用)。
例如:create index idx01_datafeed_trans On datafeed_trans(feedid,feedDate)INCLUDE(acctNo,tradeDate)
如果计划稳定或您可以稳定它,您可以使用sp_executesql('sql sentence')执行该句子以保存并使用固定的执行计划。
如果计划不稳定,您必须使用临时声明或EXEC('sql sentence')来评估和创建每次执行计划。 (或存储过程“with recompile”)。
希望能帮助到你。
提到这个问题,但有一个解释,似乎没有人考虑过。
统计 - 统计数据不可用或具有误导性
如果满足以下所有条件:
然后sql server可能错误地假设列是不相关的,导致低于预期的基数估计值来应用这两个限制并且选择了差的执行计划。这种情况下的修复方法是创建一个链接两列的统计对象,这不是一项昂贵的操作。