我遇到了一种奇怪的情况,向查询添加OPTION (RECOMPILE)会导致它在半秒内运行,而省略它会导致查询花费远远超过五分钟的时间。

当查询从查询分析器或通过sqlcommand . executerreader()从我的c#程序执行时,就是这种情况。调用(或不调用)DBCC FREEPROCCACHE或DBCC dropcleanbuffers没有区别;使用OPTION (RECOMPILE)查询结果总是立即返回,不使用则超过5分钟。查询总是使用相同的参数(为了测试)调用。

我使用的是SQL Server 2008。

I'm fairly comfortable with writing SQL but have never used an OPTION command in a query before and was unfamiliar with the whole concept of plan caches until scanning the posts on this forum. My understanding from the posts is that OPTION (RECOMPILE) is an expensive operation. It apparently creates a new lookup strategy for the query. So why is it then, that subsequent queries that omit the OPTION (RECOMPILE) are so slow? Shouldn't the subsequent queries be making use of the lookup strategy that was computed on the previous call which included the recompilation hint?

在每个调用上都需要重新编译提示的查询是否非常罕见?

抱歉问了这么初级的问题,但我真的搞不懂。

更新:我被要求张贴查询…

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)

当从Query Analyzer运行测试时,我加了以下几行代码:

declare @feedID int
select @feedID=20

declare @feedDate datetime
select @feedDate='1/2/2009'

当从我的c#程序调用它时,参数是通过SqlCommand传入的。参数属性。

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


有时使用OPTION(RECOMPILE)是有意义的。根据我的经验,只有在使用动态SQL时才有这种可行的选择。在你探索这在你的情况下是否有意义之前,我建议重建你的统计数据。这可以通过运行以下命令来完成:

EXEC sp_updatestats

然后重新创建你的执行计划。这将确保在创建执行计划时使用最新的信息。

每次执行查询时,添加OPTION(RECOMPILE)都会重新构建执行计划。我从来没有听说过创建一个新的查找策略,但也许我们只是用不同的术语来描述同一件事。

When a stored procedure is created (I suspect you are calling ad-hoc sql from .NET but if you are using a parameterized query then this ends up being a stored proc call) SQL Server attempts to determine the most effective execution plan for this query based on the data in your database and the parameters passed in (parameter sniffing), and then caches this plan. This means that if you create the query where there are 10 records in your database and then execute it when there are 100,000,000 records the cached execution plan may no longer be the most effective.

总之,我不认为OPTION(RECOMPILE)有任何好处。我猜你只需要更新你的统计数据和执行计划。根据您的情况,重新构建统计信息可能是DBA工作的重要部分。如果你在更新数据后仍然有问题,我建议你发布两个执行计划。

为了回答您的问题——是的,我想说,对于您的最佳选择来说,每次执行查询时都重新编译执行计划是非常不寻常的。


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

STATISTICS - Statistics are out of date. A database stores statistics on the range and distribution of the types of values in various column on tables and indexes. This helps the query engine to develop a "Plan" of attack for how it will do the query, for example the type of method it will use to match keys between tables using a hash or looking through the entire set. You can call Update Statistics on the entire database or just certain tables or indexes. This slows down the query from one run to another because when statistics are out of date, its likely the query plan is not optimal for the newly inserted or changed data for the same query (explained more later below). It may not be proper to Update Statistics immediately on a Production database as there will be some overhead, slow down and lag depending on the amount of data to sample. You can also choose to use a Full Scan or Sampling to update Statistics. If you look at the Query Plan, you can then also view the statistics on the Indexes in use such using the command DBCC SHOW_STATISTICS (tablename, indexname). This will show you the distribution and ranges of the keys that the query plan is using to base its approach on. PARAMETER SNIFFING - The query plan that is cached is not optimal for the particular parameters you are passing in, even though the query itself has not changed. For example, if you pass in a parameter which only retrieves 10 out of 1,000,000 rows, then the query plan created may use a Hash Join, however if the parameter you pass in will use 750,000 of the 1,000,000 rows, the plan created may be an index scan or table scan. In such a situation you can tell the SQL statement to use the option OPTION (RECOMPILE) or an SP to use WITH RECOMPILE. To tell the Engine this is a "Single Use Plan" and not to use a Cached Plan which likely does not apply. There is no rule on how to make this decision, it depends on knowing the way the query will be used by users. INDEXES - Its possible that the query haven't changed, but a change elsewhere such as the removal of a very useful index has slowed down the query. ROWS CHANGED - The rows you are querying drastically changes from call to call. Usually statistics are automatically updated in these cases. However if you are building dynamic SQL or calling SQL within a tight loop, there is a possibility you are using an outdated Query Plan based on the wrong drastic number of rows or statistics. Again in this case OPTION (RECOMPILE) is useful. THE LOGIC Its the Logic, your query is no longer efficient, it was fine for a small number of rows, but no longer scales. This usually involves more indepth analysis of the Query Plan. For example, you can no longer do things in bulk, but have to Chunk things and do smaller Commits, or your Cross Product was fine for a smaller set but now takes up CPU and Memory as it scales larger, this may also be true for using DISTINCT, you are calling a function for every row, your key matches don't use an index because of CASTING type conversion or NULLS or functions... Too many possibilities here.

In general when you write a query, you should have some mental picture of roughly how certain data is distributed within your table. A column for example, can have an evenly distributed number of different values, or it can be skewed, 80% of the time have a specific set of values, whether the distribution will varying frequently over time or be fairly static. This will give you a better idea of how to build an efficient query. But also when debugging query performance have a basis for building a hypothesis as to why it is slow or inefficient.


要添加到OPTION(RECOMPILE)非常有用的情况的优秀列表(由@CodeCowboyOrg给出),

Table Variables. When you are using table variables, there will not be any pre-built statistics for the table variable, often leading to large differences between estimated and actual rows in the query plan. Using OPTION(RECOMPILE) on queries with table variables allows generation of a query plan that has a much better estimate of the row numbers involved. I had a particularly critical use of a table variable that was unusable, and which I was going to abandon, until I added OPTION(RECOMPILE). The run time went from hours to just a few minutes. That is probably unusual, but in any case, if you are using table variables and working on optimizing, it's well worth seeing whether OPTION(RECOMPILE) makes a difference.


调优查询之前的第一个操作是对索引和统计数据进行碎片整理/重新构建,否则就是在浪费时间。

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

举个例子: 创建索引idx01_datafeed_trans 关于datafeed_trans (feeddid, feedDate) INCLUDE(acctNo,交易日期)

如果计划是稳定的,或者你可以稳定它,你可以使用sp_executesql(' SQL语句')来执行句子,以保存并使用固定的执行计划。

如果计划是不稳定的,你必须使用一个特别的语句或EXEC(“sql语句”)来评估和创建一个执行计划。(或“带有重新编译”的存储过程)。

希望能有所帮助。


这个问题有点死气沉沉的,但有一个似乎没人考虑过的解释。

统计数据-统计数据不可用或具有误导性

如果下列条件都成立:

feeddid列和feedDate列可能是高度相关的(例如,提要id比提要日期更具体,日期参数是冗余信息)。 不存在两个列都是连续列的索引。 没有手动创建涵盖这两列的统计信息。

然后,sql server可能错误地假设列是不相关的,从而导致应用限制和选择糟糕的执行计划时的基数估计低于预期。在这种情况下,修复方法是创建一个连接这两列的统计对象,这不是一个昂贵的操作。


OPTION (RECOMPILE)用于真实的单词生成场景。我用它来消除参数的气味和优化大型查询。这可能是你问题的答案,但有迹象表明,优化未知(如局部变量)也可以解决问题。

我当然不会仅仅因为几年前修复了一个时间错误就避免这个选项。OPTION (RECOMPILE)的主要风险是使用不当,例如高频请求。