web-dev-qa-db-ja.com

OPTION(RECOMPILE)は常に高速です。どうして?

クエリにOPTION (RECOMPILE)を付け加えると0.5秒で実行されるという奇妙な状況に遭遇しましたが、それを省略するとクエリに5分以上かかることがありました。

これは、クエリがQuery AnalyzerまたはC#プログラムからSqlCommand.ExecuteReader()を介して実行される場合です。 DBCC FREEPROCCACHEまたはDBCC dropcleanbuffersを呼び出してもしなくても違いはありません。クエリ結果は常にOPTION (RECOMPILE)で即座に返され、5分以上返されません。クエリは常に同じパラメータで呼び出されます(このテストのために)。

SQL Server 2008を使用しています。

私はかなりSQLを書くことに慣れていますが、これまでクエリでOPTIONコマンドを使用したことがなく、このフォーラムの投稿をスキャンするまではプランキャッシュの概念全体に慣れていませんでした。投稿からの私の理解はOPTION (RECOMPILE)は高価な操作であるということです。それは明らかにクエリのための新しいルックアップ戦略を作成します。では、なぜOPTION (RECOMPILE)を省略した後続のクエリがとても遅いのでしょうか。後続のクエリは、再コンパイルのヒントを含む前回の呼び出しで計算されたルックアップ戦略を利用するべきではないでしょうか。

1回の呼び出しごとに再コンパイルのヒントを必要とするクエリがあるのは非常に珍しいですか?

初級レベルの質問ですみませんが、私は本当にこれの頭か尾を作ることができません。

更新:私は質問を投稿するように頼まれました...

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.Parametersプロパティを通して渡されます。

この議論の目的のために、あなたはパラメータが決して変化しないと仮定することができます、それで我々は原因として最適以下のパラメータ臭いを除外することができます。

141
Chad Decker

OPTION(RECOMPILE)を使うのが理にかなっていることがあります。私の経験上、これが実行可能な選択肢となるのは、動的SQLを使用しているときだけです。これがあなたの状況で意味があるかどうかを探る前に、私はあなたの統計を再構築することを勧めます。これは、次のコマンドを実行することによって実行できます。

EXEC sp_updatestats

そして実行計画を作り直します。これにより、実行計画が作成されたときに最新の情報が使用されるようになります。

OPTION(RECOMPILE)を追加すると、クエリが実行されるたびに実行プランが再構築されます。私はcreates a new lookup strategyと記述されていると聞いたことは一度もありませんが、おそらく同じことに別の用語を使用しているだけかもしれません。

ストアドプロシージャが作成されるとき(私はあなたが.NETからアドホックSQLを呼んでいると思うが あなたがパラメータ化されたクエリを使用しているなら、これはストアドプロシージャコールであることになる )SQL Serverは、データベース内のデータと( パラメータスニッフィング で渡されたパラメータに基づいて、このクエリに対して最も効果的な実行計画を決定しようとします。 ))その後、この計画をキャッシュします。つまり、データベース内のレコードが10個あるところでクエリを作成し、レコードが100,000,000個あるときにクエリを実行すると、キャッシュされた実行計画が最も効果的ではなくなる可能性があります。

要約すると、ここでOPTION(RECOMPILE)がメリットになるとは思われません。統計と実行計画を更新するだけでいいのではないでしょうか。状況に応じて、統計の再構築はDBA作業に不可欠な部分になる可能性があります。統計を更新しても問題が解決しない場合は、両方の実行計画を投稿することをお勧めします。

そしてあなたの質問に答えるために - はい、あなたがクエリを実行するたびに実行プランを再コンパイルすることがあなたの最善の選択肢にとって非常に珍しいと思います。

136
Abe Miessler

多くの場合、クエリの実行ごとに大きな違いがあるとき、私はそれがしばしば5つの問題のうちの1つであると思います。

  1. STATISTICS - 統計は古くなっています。データベースは、テーブルとインデックスのさまざまなカラムに値の範囲の分布と分布に関する統計を格納します。これは、クエリエンジンがどのようにクエリを実行するかについての「計画」攻撃を開発するのに役立ちます。たとえば、ハッシュを使用してテーブル全体のキーを照合するために使用するメソッドの種類などです。データベース全体または特定のテーブルまたはインデックスに対してUpdate Statisticsを呼び出すことができます。統計が古くなると、同じクエリに対して新しく挿入または変更されたデータに対してクエリプランが最適ではなくなる可能性があるため、実行ごとにクエリが遅くなります(後で詳しく説明します)。サンプリングするデータ量によってはオーバーヘッドが発生したり、速度が遅くなったり遅れたりするため、本番データベースで統計をすぐに更新するのは適切ではない場合があります。フルスキャンまたはサンプリングを使用して統計を更新することもできます。クエリプランを見ると、コマンドDBCC SHOW_STATISTICS(tablename、indexname)を使用して、使用中のインデックスに関する統計を表示することもできます。これにより、クエリプランがそのアプローチの基礎となるキーの分布と範囲がわかります。

  2. PARAMETER SNIFFING - キャッシュされているクエリプランは、クエリ自体は変更されていなくても、渡している特定のパラメータには最適ではありません。たとえば、1,000,000行のうち10行しか取得しないパラメータを渡すと、作成されたクエリプランはハッシュ結合を使用できますが、渡されたパラメータが1,000,000行のうち750,000行を使用する場合、作成されたプランはインデックススキャンまたはテーブルスキャン。このような状況では、WITH RECOMPILEを使用するために、オプションOPTION(RECOMPILE)またはSPを使用するようにSQLステートメントに指示できます。エンジンに伝えるために、これは「シングルユースプラン」であり、おそらく適用されないキャッシュプランを使用しないようにします。どのようにこの決定をするかについての規則はありません、それは質問がユーザによって使用される方法を知ることに依存します。

  3. INDEXES - クエリが変更されていない可能性がありますが、非常に有用なインデックスの削除など、他の場所での変更によってクエリが遅くなっている可能性があります。

  4. ROWS CHANGED - 問い合わせている行は呼び出しごとに劇的に変わります。通常、統計は自動的に更新されます。ただし、動的SQLを構築している場合、または厳密なループ内でSQLを呼び出している場合は、誤った急激な行数または統計に基づいて古いクエリプランを使用している可能性があります。この場合も、OPTION(RECOMPILE)が便利です。

  5. THE LOGICそのロジックは、あなたのクエリはもはや効率的ではない、それは少数の行のためにそれは大丈夫だったが、もはやスケールしません。これには通常、クエリプランのより詳細な分析が含まれます。たとえば、物事をまとめて行うことはできませんが、物事をまとめて小さなコミットをしなければならない、またはクロス製品は小さなセットで問題ありませんでしたが、スケールが大きくなるにつれてCPUとメモリを占有します。 DISTINCTを使用すると、すべての行に対して関数を呼び出していることになります。CATTING型変換やNULLまたは関数のため、キーマッチではインデックスを使用しません。可能性が多すぎます。

一般的に、クエリを書くときには、テーブル内で特定のデータがどのように分散されているかについて、いくつかの精神的なイメージがあります。たとえば、列は、均等に分散された数の異なる値を持つことができます。あるいは、分布が時間とともに頻繁に変化するか、かなり静的であるかにかかわらず、80%の時間に特定の値のセットがあります。これにより、効率的なクエリを作成する方法についてのより良いアイデアが得られます。しかし、クエリのパフォーマンスをデバッグするときには、それがなぜ遅いまたは非効率的であるかについての仮説を立てるための基礎があります。

124
CodeCowboyOrg

OPTION(RECOMPILE)が非常に役立つ可能性がある状況の優れたリスト(@CodeCowboyOrgによって提供される)に追加するには、

  1. テーブル変数。テーブル変数を使用している場合は、テーブル変数の事前に作成された統計はなく、クエリプランの推定行と実際の行に大きな違いが生じることがよくあります。テーブル変数を使用したクエリでOPTION(RECOMPILE)を使用すると、関係する行番号をより正確に見積もったクエリプランを生成できます。私はOPTION(RECOMPILE)を追加するまで、使用できないテーブル変数を特に批判的に使用していました。実行時間は数時間からわずか数分になりました。これはおそらく珍しいことですが、いずれにせよ、テーブル変数を使用して最適化を行っている場合は、OPTION(RECOMPILE)が効果を生み出すかどうかを確認する価値があります。
24
DWright

クエリをチューニングする前の最初のアクションは、インデックスと統計をデフラグ/再構築することです。

実行計画をチェックして、それが安定しているかどうかを確認する必要があります(パラメータを変更した場合も同じです)。そうでない場合は、カバーインデックスを作成する必要があります。他のクエリにも便利です。

例として、create index idx01_datafeed_trans on datafeed_trans(feedid、feedDate)INCLUDE(acctNo、tradeDate)

計画が安定しているか安定している場合は、sp_executesql( 'sql sentence')を使用して文を実行し、固定の実行計画を保存して使用できます。

計画が不安定な場合は、毎回実行計画を評価して作成するためにアドホックステートメントまたはEXEC( 'sql sentence')を使用する必要があります。 (またはストアドプロシージャ "with recompile").

それが役に立てば幸い。

この質問を却下したが、誰も考えていなかったような説明がある。

統計 - 統計が利用できないか誤解を招く

以下のすべてが当てはまる場合:

  1. Feedid列とfeedDate列は、高度に相関している可能性があります(たとえば、フィードIDはフィード日付よりも具体的で、日付パラメーターは冗長な情報です)。
  2. 両方の列を順次列として持つ索引はありません。
  3. これら両方の列をカバーする手動で作成された統計はありません。

その場合、SQL Serverは、列が無相関であると誤って想定している可能性があり、両方の制限を適用した場合のカーディナリティーの見積もりが予想よりも低くなり、実行計画が不良になります。この場合の修正方法は、2つの列をリンクする統計オブジェクトを作成することです。これはコストのかかる操作ではありません。

0