我遇到了一个奇怪的情况,我的查询附加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
属性传入。
出于本讨论的目的,您可以假设参数永远不会改变,因此我们可以排除次优参数嗅觉作为原因。
答案 0 :(得分:136)
有时使用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工作的重要组成部分。如果您在更新统计数据后仍然遇到问题,我建议发布两个执行计划。
回答你的问题 - 是的,我会说你每次执行查询时都要重新编译执行计划是非常不寻常的。
答案 1 :(得分:124)
通常,当查询的运行与运行之间存在巨大差异时,我发现它通常是5个问题中的一个。
ç计 - 统计数据已过期。数据库存储有关表和索引的各列中值类型的范围和分布的统计信息。这有助于查询引擎开发一个"计划"攻击如何进行查询,例如它将使用散列或查看整个集合来匹配表之间的键的方法类型。您可以在整个数据库或仅某些表或索引上调用Update Statistics。这会将查询从一次运行减慢到另一次运行,因为当统计信息过期时,查询计划可能不适合同一查询的新插入或更改的数据(稍后将详细说明)。在生产数据库上立即更新统计信息可能不合适,因为会有一些开销,减速和滞后,具体取决于要采样的数据量。您还可以选择使用“完全扫描”或“采样”来更新统计信息。如果查看查询计划,您还可以使用命令 DBCC SHOW_STATISTICS(tablename,indexname)查看正在使用的索引的统计信息。这将显示查询计划用于基于其方法的键的分布和范围。
PARAMETER SNIFFING - 即使查询本身未更改,缓存的查询计划也不是您传入的特定参数的最佳选择。例如,如果传入的参数仅检索1,000,000行中的10个,则创建的查询计划可能使用散列连接,但是如果传入的参数将使用1,000,000行中的750,000行,则创建的计划可能是索引扫描或表扫描。在这种情况下,您可以告诉SQL语句使用选项 OPTION(RECOMPILE)或SP来使用WITH RECOMPILE。告诉引擎这是一个"单一使用计划"而不是使用可能不适用的缓存计划。关于如何做出这个决定没有规则,这取决于知道用户使用查询的方式。
INDEXES - 查询可能无法更改,但其他地方的更改(例如删除非常有用的索引)会降低查询速度。
ROWS CHANGED - 您要查询的行会因呼叫而发生剧烈变化。通常在这些情况下会自动更新统计信息。但是,如果要在紧密循环中构建动态SQL或调用SQL,则可能会使用基于错误的行数或统计信息的过时查询计划。同样在这种情况下, OPTION(RECOMPILE)非常有用。
THE LOGIC 它的逻辑,你的查询不再有效,它适用于少量行,但不再扩展。这通常涉及对查询计划进行更深入的分析。例如,您不能再批量处理,但必须使用Chunk并执行较小的Commits,或者您的Cross Product适用于较小的集合,但现在占用CPU和内存,因为它扩展得更大,这也可能适用于使用DISTINCT,你为每一行调用一个函数,你的密钥匹配因为CASTING类型转换或NULLS或函数而不使用索引......这里有太多可能性。
通常,当您编写查询时,您应该大致了解某些数据在表中的分布情况。例如,列可以具有均匀分布的不同值的数量,或者它可以是倾斜的,80%的时间具有一组特定的值,无论分布是否会随着时间频繁变化或者是相当静态的。这将使您更好地了解如何构建有效的查询。但是,当调试查询性能有一个基础,可以建立一个关于它为什么慢或低效的假设。
答案 2 :(得分:24)
要添加OPTION(RECOMPILE)非常有帮助的优秀列表(由@CodeCowboyOrg提供),
答案 3 :(得分:0)
调整查询之前的第一个操作是碎片整理/重建索引和统计信息,否则你会浪费时间。
您必须检查执行计划以确定它是否稳定(更改参数时是否相同),否则,您可能必须创建封面索引(在本例中为每个表)(知道你可以创建一个对其他查询也有用的系统。)
作为一个例子: 创建索引idx01_datafeed_trans 在datafeed_trans(feedid,feedDate) INCLUDE(acctNo,tradeDate)
如果计划稳定或者您可以稳定它,您可以使用sp_executesql(' sql语句')执行该句子以保存和使用固定的执行计划。
如果计划不稳定,您必须使用临时声明或EXEC(' sql语句')来评估和创建每次执行计划。 (或存储过程"重新编译")。
希望它有所帮助。
答案 4 :(得分:0)
解决了这个问题,但有一种解释似乎没有人考虑过。
统计信息-统计信息不可用或具有误导性
如果满足以下所有条件:
然后,sql服务器可能会错误地假设这些列是不相关的,从而导致应用限制和选择不良执行计划的基数估计值低于预期。在这种情况下,解决方法是创建一个链接两列的统计对象,这不是一个昂贵的操作。