选项(RECOMPILE)总是更快; 为什么?

我遇到了一个奇怪的情况,追加OPTION (RECOMPILE)到我的查询导致它运行在半秒钟,而省略它导致查询花费超过五分钟。

查询分析器或从我的C#程序通过SqlCommand.ExecuteReader()执行查询时就是这种情况。 调用(或不调用) DBCC FREEPROCCACHEDBCC dropcleanbuffers没有区别; 查询结果总是立即用OPTION (RECOMPILE)返回,并且在没有它的情况下大于五分钟。 查询总是用相同的参数调用[为了这个testing]。

我正在使用SQL Server 2008。

我对编写SQL相当舒服,但从未在查询中使用过OPTION命令,并且对计划caching的整个概念不熟悉,直到扫描此论坛上的post。 我从post的理解是OPTION (RECOMPILE)是一个昂贵的操作。 它显然为查询创build了一个新的查找策略。 那么为什么接下来的查询忽略了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) 

当从查询分析器运行testing时,我在前面加上以下几行:

 declare @feedID int select @feedID=20 declare @feedDate datetime select @feedDate='1/2/2009' 

从我的C#程序调用时,参数通过SqlCommand.Parameters属性传入。

为了这个讨论的目的,你可以假设参数不会改变,所以我们可以排除次优参数闻的原因。

有时候使用OPTION(RECOMPILE)是有道理的。 根据我的经验,唯一一个可行的select是使用dynamicSQL。 在你研究这种情况是否合理的情况下,我build议你重build你的统计数据。 这可以通过运行以下来完成:

 EXEC sp_updatestats 

然后重新创build您的执行计划。 这将确保当你的执行计划被创build时,它将使用最新的信息。

每次执行查询时,添加OPTION(RECOMPILE)重新生成执行计划。 我从来没有听说过描述creates a new lookup strategy但也许我们只是使用不同的术语为同一件事情。

当创build一个存储过程时(我怀疑你是从.NET调用即席SQL,但是如果你正在使用参数化查询,那么这最终是一个存储过程调用 )SQL Server试图确定这个查询的最有效的执行计划根据数据库中的数据和传入的参数( 参数嗅探 ),然后caching这个计划。 这意味着,如果在数据库中有10条logging的情况下创build查询,并在有100,000,000条logging时执行该查询,则caching的执行计划可能不再是最有效的。

总之 – 我没有看到任何理由, OPTION(RECOMPILE)将是一个好处在这里。 我怀疑你只需要更新你的统计数据和你的执行计划。 根据您的情况重build统计数据可能是DBA工作的重要组成部分。 如果在更新统计信息后仍然有问题,我会build议发布两个执行计划。

并回答你的问题 – 是的,我会说,你最好的select是每次执行查询时重新编译执行计划。

通常,当查询运行到运行有很大差异时,我发现它通常是5个问题之一。

  1. 统计 – 统计数据已过期。 数据库存储关于表和索引的不同列中值的types范围和分布的统计信息。 这有助于查询引擎针对如何执行查询来开发攻击的“计划”,例如,将使用用于使用散列来匹配表之间的键或查看整个集合的方法的types。 您可以在整个数据库或只是某些表或索引上调用更新统计信息。 这会减慢从一个运行到另一个运行的查询,因为当统计数据过期时,对于相同查询的新插入或更改的数据,其可能的查询计划可能不是最佳的(稍后将在后面解释)。 在生产数据库上立即更新统计数据可能是不适当的,因为会有一些开销,减慢和滞后,这取决于要采样的数据量。 您也可以select使用全面扫描或采样来更新统计信息。 如果查看查询计划,则可以使用命令DBCC SHOW_STATISTICS(tablename,indexname)查看正在使用的索引的统计信息。 这将显示查询计划正在使用的关键字的分布和范围。

  2. PARAMETER SNIFFING – 即使查询本身没有改变,被caching的查询计划对于传入的特定参数也不是最优的。 例如,如果您传递的参数只能检索1,000,000行中的10个,则创build的查询计划可能会使用散列连接,但是如果传入的参数将使用1,000,000行中的750,000个,则创build的计划可能是索引扫描或表扫描。 在这种情况下,您可以告诉SQL语句使用OPTION(RECOMPILE)选项或SP使用WITH RECOMPILE。 告诉引擎这是一个“一次性使用计划”,而不是使用可能不适用的caching计划。 如何做出这个决定是没有规定的,这取决于知道用户使用查询的方式。

  3. INDEXES – 有可能查询没有改变,但其他地方的改变,如删除非常有用的索引,已经减慢了查询速度。

  4. ROWS CHANGED – 你正在查询的行急剧变化,从呼叫到呼叫。 通常在这些情况下统计数据会自动更新。 但是,如果您正在构builddynamicSQL或在紧密循环中调用SQL,则有可能基于错误的行数或统计信息数量过大而使用过时的查询计划。 在这种情况下, OPTION(RECOMPILE)也是有用的。

  5. 逻辑它的逻辑,你的查询不再有效率,对于less量的行很好,但不再是规模。 这通常涉及更深入的查询计划分析。 例如,你不能再大批量地做事情,但是不得不做大块的事情,做更小的提交,或者你的交叉产品对于一个较小的集合来说是好的,但是现在占用CPU和内存,因为它扩大了,这也可能是真实的使用DISTINCT,你正在为每一行调用一个函数,因为CASTINGtypes转换或NULLS或函数,你的键匹配不使用索引…在这里太多的可能性。

一般来说,当你编写一个查询时,你应该对你的表中的特定数据进行粗略的分析。 例如,列可以具有不同数值的均匀分布,或者可以是倾斜的,80%的时间具有特定的一组值,不pipe分布随时间变化频繁还是相当静态。 这将使您更好地了解如何构build高效的查询。 而且,当debugging查询性能时,有一个基础来build立一个为什么它是慢或低效率的假设。

要添加到极好的列表(由@CodeCowboyOrg给出)的情况​​下,选项(RECOMPILE)可以是非常有用的,

  1. 表格variables 。 在使用表variables时,表variables不会有任何预先build立的统计信息,通常会导致查询计划中估计行和实际行之间的差异较大。 对带有表variables的查询使用OPTION(RECOMPILE)允许生成一个查询计划,该计划可以更好地估计所涉及的行数。 我有一个特别关键的使用表variables是无法使用,而我将放弃,直到我添加选项(重build)。 运行时间从几小时到几分钟。 这可能是不寻常的,但无论如何,如果你正在使用表variables和优化工作,那么很有必要看看OPTION(RECOMPILE)是否有所作为。

调整查询之前的第一个操作是整理/重build索引和统计信息,否则就是在浪费时间。

你必须检查执行计划,看看它是否稳定(当你改变参数时是一样的),如果没有,你可能不得不创build一个封面索引(在这种情况下,每个表)(知道系统,你可以创build一个对于其他查询也是有用的)。

作为一个例子:创build索引idx01_datafeed_trans在datafeed_trans(feedid,feedDate) INCLUDE(acctNo,tradeDate)

如果计划是稳定的,或者你可以稳定它,你可以用sp_executesql('sql sentence')来执行这个句子来保存和使用一个固定的执行计划。

如果计划不稳定,则必须使用专用语句或EXEC(“sql语句”)来评估和创build每次执行计划。 (或“重新编译”的存储过程)。

希望能帮助到你。