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

Cha*_*ker 156 sql sql-server compilation hint sql-server-2008

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

从查询分析器或我的C#程序执行查询时就是这种情况SqlCommand.ExecuteReader().打电话(或不打电话)DBCC FREEPROCCACHEDBCC 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)
Run Code Online (Sandbox Code Playgroud)

从查询分析器运行测试时,我预先添加以下行:

declare @feedID int
select @feedID=20

declare @feedDate datetime
select @feedDate='1/2/2009'
Run Code Online (Sandbox Code Playgroud)

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

出于本讨论的目的,您可以假设参数永远不会改变,因此我们可以排除次优参数嗅觉作为原因.

Abe*_*ler 144

有时使用OPTION(RECOMPILE)是有道理的.根据我的经验,这是一个可行的选项,唯一一次是使用动态SQL.在您探究在您的情况下这是否有意义之前,我建议您重建统计数据.这可以通过运行以下命令来完成:

EXEC sp_updatestats
Run Code Online (Sandbox Code Playgroud)

然后重新创建执行计划.这将确保在创建执行计划时,它将使用最新信息.

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

创建存储过程时(我怀疑你是从.NET调用ad-hoc sql但是如果你使用的是参数化查询,那么最终会成为存储过程调用)SQL Server会尝试确定此查询的最有效执行计划根据数据库中的数据和传入的参数(参数嗅探),然后缓存此计划.这意味着如果您创建查询,其中数据库中有10条记录,然后在有100,000,000条记录时执行它,则缓存的执行计划可能不再是最有效的.

总而言之 - 我认为没有任何理由OPTION(RECOMPILE)可以带来好处.我怀疑你只需要更新统计数据和执行计划.根据您的具体情况,重建统计数据可能是DBA工作的重要组成部分.如果您在更新统计数据后仍然遇到问题,我建议发布两个执行计划.

并回答你的问题 - 是的,我会说你每次执行查询时都要重新编译执行计划是非常不寻常的.

  • 是的,sp_updatestats做了这个伎俩.当你提到一个查询最初在一个包含10条记录的表上运行时,你在头上钉了一针,现在该表有数百万条记录.这完全是我的情况.我没有在帖子中提到它,因为我认为这不重要.有趣的东西.再次感谢. (20认同)
  • 一个有趣的细节:更新统计信息隐式地使使用这些统计信息的所有缓存计划无效,但仅限于[统计信息在更新操作后实际更改](https://www.brentozar.com/archive/2015/01/updating-statistics-cause -recompile-无数据改变/).因此,对于高度倾斜的只读表,似乎一个明确的"OPTION(RECOMPILE)"可能是唯一的解决方案. (4认同)
  • 这是我发现使用表变量的唯一方法,因为SQL总是认为只有一行.当它包含几千行时,它就成了一个问题. (2认同)

Cod*_*Org 131

通常,当查询的运行与运行之间存在巨大差异时,我发现它通常是5个问题中的一个.

  1. 统计 - 统计数据已过时.数据库存储有关表和索引的各列中值类型的范围和分布的统计信息.这有助于查询引擎针对如何进行查询来制定攻击的"计划",例如,它将使用散列或查看整个集合来匹配表之间的键的方法类型.您可以在整个数据库或仅某些表或索引上调用Update Statistics.这会将查询从一次运行减慢到另一次运行,因为当统计信息过期时,查询计划可能不适合同一查询的新插入或更改的数据(稍后将详细说明).在生产数据库上立即更新统计信息可能不合适,因为会有一些开销,减速和滞后,具体取决于要采样的数据量.您还可以选择使用"完全扫描"或"采样"来更新统计信息.如果查看查询计划,则还可以使用命令DBCC SHOW_STATISTICS(tablename,indexname)查看正在使用的索引的统计信息.这将显示查询计划用于基于其方法的键的分布和范围.

  2. PARAMETER SNIFFING - 即使查询本身没有更改,缓存的查询计划也不是您传入的特定参数的最佳选择.例如,如果传入的参数仅检索1,000,000行中的10个,则创建的查询计划可能使用散列连接,但是如果传入的参数将使用1,000,000行中的750,000行,则创建的计划可能是索引扫描或表扫描.在这种情况下,您可以告诉SQL语句使用选项OPTION(RECOMPILE)或SP来使用WITH RECOMPILE.告诉引擎这是一个"单一使用计划",而不是使用可能不适用的缓存计划.关于如何做出这个决定没有规则,这取决于知道用户使用查询的方式.

  3. INDEXES - 查询可能没有改变,但是其他地方的更改(例如删除非常有用的索引)会降低查询速度.

  4. ROWS CHANGED - 您要查询的行会因呼叫而大幅变化.通常在这些情况下会自动更新统计信息.但是,如果要在紧密循环中构建动态SQL或调用SQL,则可能会使用基于错误的行数或统计信息的过时查询计划.同样在这种情况下,OPTION(RECOMPILE)很有用.

  5. 逻辑它的逻辑,你的查询不再有效,它适用于少量行,但不再是缩放.这通常涉及对查询计划进行更深入的分析.例如,您不能再批量处理,但必须使用Chunk的东西并进行较小的Commits,或者您的Cross Product适用于较小的集合,但现在占用CPU和内存,因为它扩展得更大,这也可能适用于使用DISTINCT,您正在为每一行调用一个函数,由于CASTING类型转换或NULLS或函数,您的密钥匹配不使用索引......此处有太多可能性.

通常,当您编写查询时,您应该大致了解某些数据在表中的分布情况.例如,一列可以具有均匀分布的不同值的数量,或者它可以是倾斜的,80%的时间具有一组特定的值,无论分布是否会随着时间频繁变化或者是相当静态的.这将使您更好地了解如何构建有效的查询.但是,当调试查询性能有一个基础来建立一个假设,为什么它是缓慢或低效.

  • 谢谢你,朋友。这是极好的信息。当我最初发布我的问题时,我无法理解您的回答,但现在对我来说很有意义。 (2认同)
  • PARAMETER SNIFFING是迄今为止我存在的最大祸害.在面试失败之前我甚至都不知道这个命令.我对参数嗅探的解决方案一直是对参数值进行散列并附加"AND {hash} = {hash}",以便sql对于不同的值总是不同的.一个黑客,但它的工作. (2认同)

DWr*_*ght 25

要添加OPTION(RECOMPILE)非常有用的优秀列表(由@CodeCowboyOrg提供),

  1. 表变量.使用表变量时,表变量不会有任何预先构建的统计信息,通常会导致查询计划中估计行与实际行之间存在较大差异.对表变量的查询使用OPTION(RECOMPILE)允许生成一个查询计划,该计划可以更好地估计所涉及的行数.我特别关键地使用了一个无法使用的表变量,而且我要放弃它,直到我添加了OPTION(RECOMPILE).运行时间从几小时到几分钟.这可能是不寻常的,但无论如何,如果你使用表变量并进行优化,那么值得看看OPTION(RECOMPILE)是否有所作为.

  • 临时表中的统计信息不会自动更新或重新编译,因此程序员需要这样做. (2认同)