Statistics

適用対象:SQL ServerAzure SQL DatabaseAzure SQL Managed InstanceAzure Synapse AnalyticsMicrosoft Fabric SQL Database

クエリ オプティマイザーでは、クエリのパフォーマンスを向上させるクエリ プランを作成するために統計を使用します。 ほとんどのクエリでは、高品質のクエリ プランに必要な統計がクエリ オプティマイザーによって既に生成されています。 場合によっては、最適な結果を得るために、追加の統計を作成したり、クエリデザインを変更したりする必要があります。 この記事では、統計の概念について説明し、クエリ最適化の統計を効果的に使用するためのガイドラインを提供します。

コンポーネントおよび概念

Statistics

クエリ最適化に関する統計は、テーブルまたはインデックス付きビューの 1 つまたは複数の列の値の分布に関する統計情報を格納するバイナリ ラージ オブジェクト (BLOB) です。 クエリ オプティマイザーでは、これらの統計を使用してクエリ結果のカーディナリティ、つまり行数を推定します。 これらのカーディナリティの推定に基づいて、クエリ オプティマイザーでは高品質なクエリ プランを作成できます。 たとえば、ご利用の述語によっては、クエリ オプティマイザーがカーディナリティの推定を使用して、リソース消費の多い Index Scan 操作ではなく Index Seek 操作を選択することがあります。そうすることで、クエリのパフォーマンスが高まります。

統計オブジェクトは 1 つ以上のテーブル列で構成されるリストごとに作成され、それぞれに最初の列の値の分布を示すヒストグラムが含まれます。 複数列の統計オブジェクトには、さらに、列間の値の相関関係に関する統計情報も格納されます。 これらの相関関係の統計情報 ( 密度) は、個別の列値を持つ行の数から得られます。

Histogram

ヒストグラムでは、データセットの個別の値ごとに出現頻度を測定します。 クエリ オプティマイザーでは、統計オブジェクトの最初のキー列の列値に基づいてヒストグラムを計算し、行を統計的にサンプリングするかテーブルまたはビュー内のすべての行でフル スキャンを実行することによって列値を選択します。 サンプリングされた一連の行からヒストグラムが作成された場合、行の数と個別の値の数に対する格納された合計は見積もりであり、整数である必要はありません。

Note

SQL Serverは、統計オブジェクトのキー列のセットの最初の列である 1 つの列についてのみヒストグラムを作成します。

ヒストグラムを作成するには、クエリ オプティマイザーで列値を並べ替え、個別の列値ごとに一致する値の数を計算し、列値を最大 200 の連続したヒストグラム区間に集計します。 各ヒストグラム区間には、列値の範囲と上限の列値が順番に含まれます。 この範囲には、境界値の間 (境界値自体は除く) のすべての有効な列値が含まれます。 格納される最小の列値は、最初のヒストグラム区間の上限境界値になります。

具体的には、SQL Serverは、次の 3 つの区間で、並べ替えられた列値のセットからヒストグラムを作成します。

  • ヒストグラムの初期化: 最初の区間で、並べ替えられたセットの先頭から始まる値のシーケンスが処理され、range_high_keyequal_rowsrange_rowsdistinct_range_rows の最大 200 個の値が収集されます (この区間の間、range_rowsdistinct_range_rows は常にゼロです)。 最初の手順は、すべての入力が使い果たされたとき、または 200 個の値が見つかったときに終了します。
  • バケットのマージによるスキャン: 統計キーの先頭列の各追加値は、2 番目の手順で並べ替えられた順序で処理されます。 連続する各値が最後の範囲に追加されるか、末尾に新しい範囲が作成されます (入力値が並べ替えられるため、この順序が可能です)。 新しい範囲が作成されると、この処理は既存の隣接する2つの範囲を1つの範囲にまとめます。 情報の損失を最小限に抑えるために、この範囲の組が選択されます。 この方法では、区間幅を最大にするアルゴリズムを使用して境界値の差を最大にし、ヒストグラムの区間の数を最小限に抑えます。 範囲を折りたたんだ後の手順の数は、この手順全体で 200 個のままです。
  • ヒストグラムの統合: 3 番目の手順では、大量の情報が失われなければ、より多くの範囲を折りたたむことができます。 ヒストグラムの区間の数は、境界点が 200 より少ない列でも、個別の値の数より少なくなることがあります。 そのため、列に 200 を超える一意の値が含まれていても、ヒストグラムの区間の数は 200 未満となることがあります。 一意の値のみで構成される列の場合、統合ヒストグラムには少なくとも 3 つのステップがあります。

Note

ヒストグラムが fullscan ではなくサンプルを使用して構築される場合、 equal_rowsrange_rowsdistinct_range_rowsおよびaverage_range_rows の値は推定値であるため、整数である必要はありません。

次の図は、6 つの区間があるヒストグラムを示しています。 最初の上限境界値の左側にある領域が最初の区間です。

サンプリングした列の値からヒストグラムを計算する方法を示す図。

前の例の各ヒストグラム ステップについて:

  • 太字の線は、上の境界値 (range_high_key) と発生回数 (equal_rows) を表します。

  • range_high_keyの左側の塗りつぶし領域は、列の値の範囲と、各列の値が発生した平均回数 (average_range_rows) を表します。 最初のヒストグラム区間の average_range_rows は常に 0 です。

  • 点線は、範囲内の個別の値の合計数 (distinct_range_rows) と範囲内の値の合計数 (range_rows) を推定するために使用されるサンプリングされた値を表します。 クエリ オプティマイザーは、range_rowsdistinct_range_rows を使用して average_range_rows を計算し、サンプリングされた値を格納しません。

密度ベクトル

密度 は、特定の列または列の組み合わせの重複の数に関する情報であり、1/(個別の値の数) として計算されます。 クエリ オプティマイザーでは、同一のテーブルまたはインデックス付きビューから複数の列を返すクエリに対するカーディナリティの推定を向上させるために密度を使用します。 密度が減少するにつれて、値の選択度が高くなります。 たとえば、車を表すテーブルの場合、同メーカーの車がいくつもあります。ただし、VIN (車両番号) はそれぞれの車両固有のものです。 VIN 上のインデックスは、製造元でのインデックスより選択度が高くなります。これは VIN の密度が製造元の場合より低いからです。

Note

頻度とは、統計オブジェクトの最初のキー列における各個別値の発生に関する情報であり、"row count * density" の式で計算されます。 最大頻度 1 は、一意の値を持つ列で確認できます。

密度ベクトルには、統計オブジェクトの列のプレフィックスごとに 1 つの密度が格納されます。 たとえば、統計オブジェクトに CustomerIdItemId、および Priceのキー列がある場合、密度は次の各列プレフィックスで計算されます。

列プレフィックス 密度の計算対象
(CustomerId) CustomerId の値が一致する行
(CustomerIdItemId) CustomerIdItemId の値が一致する行
(CustomerIdItemIdPrice) CustomerIdItemId、および Price の値が一致する行

フィルター適用済みの統計

適切に定義されたデータのサブセットから選択するクエリでは、フィルター選択された統計情報を使用するとクエリのパフォーマンスを向上させることができます。 フィルター選択された統計情報では、統計情報に含まれるデータのサブセットを選択するためにフィルター述語を使用します。 統計情報を適切にフィルター選択すると、テーブル全体の統計情報を使用する場合と比べて、クエリ実行プランが向上します。 フィルター述語の詳細については、「 CREATE STATISTICS」を参照してください。 フィルター選択された統計情報を作成する場合の詳細については、このトピックの「統計を作成する場合」を参照してください。

統計オプション

統計を作成および更新するタイミングと方法に影響するオプションを構成できます。 これらのオプションは、データベース レベルでのみ設定できます。

AUTO_CREATE_STATISTICS オプション

統計の自動作成オプション (AUTO_CREATE_STATISTICS) をオンにすると、クエリ オプティマイザーは必要に応じてクエリ述語内の個々の列に対して統計を作成し、クエリ プランのカーディナリティ推定を向上させます。 これらの 1 列ずつの統計は、既存の統計オブジェクトにまだヒストグラムがない列について作成されます。 AUTO_CREATE_STATISTICS オプションは、データベースがインデックスの統計を作成するかどうかを決定しません。 このオプションでは、フィルター選択された統計も生成されません。 このオプションは、テーブル全体の 1 列ずつの統計にのみ適用されます。

AUTO_CREATE_STATISTICS オプションを使用した結果としてクエリ オプティマイザーによって統計が作成された場合、その統計名の先頭は _WA となります。 次のクエリを使用して、クエリ オプティマイザーがクエリ述語列の統計を作成したかどうかを判断できます。

SELECT OBJECT_NAME(s.object_id) AS object_name,
    COL_NAME(sc.object_id, sc.column_id) AS column_name,
    s.name AS statistics_name
FROM sys.stats AS s
    INNER JOIN sys.stats_columns AS sc
        ON s.stats_id = sc.stats_id
        AND s.object_id = sc.object_id
WHERE s.name LIKE '_WA%'
ORDER BY s.name;

AUTO_UPDATE_STATISTICS オプション

統計の自動更新オプション ( AUTO_UPDATE_STATISTICS) を有効にすると、クエリ オプティマイザーによって統計が古くなっている可能性があるタイミングが判断され、クエリで統計が使用されたときに更新されます。 このアクションは、統計の再コンパイルとも呼ばれます。 挿入、更新、削除、またはマージ操作による変更によって、テーブルまたはインデックス付きビューのデータの分布が変わると、統計は古くなったと判断されます。 クエリ オプティマイザーは、前回の統計更新以降の行変更の数をカウントし、その数をしきい値と比較して、統計が古くなっている可能性があるかどうかを判断します。 しきい値は、テーブルのカーディナリティ (テーブルまたはインデックス付きビューの行数) に基づいています。

行の変更に基づいて統計が古くなっていることをマークする処理は、AUTO_UPDATE_STATISTICS オプションが OFF の場合でも発生します。 AUTO_UPDATE_STATISTICS オプションがOFFされている場合、システムは統計を古い状態としてマークした場合でも、統計を更新しません。 プランでは、古い統計オブジェクトが引き続き使用されます。 AUTO_UPDATE_STATISTICSOFF に設定すると、最適ではないクエリ プランが発生し、クエリのパフォーマンスが低下する可能性があります。 AUTO_UPDATE STATISTICS オプションをONに設定します。

  • SQL Server 2014 (12.x) までのバージョンでは、統計が評価された時点のテーブルまたはインデックス付きビューの行数に基づいて、データベース エンジンが再コンパイルのしきい値を使用します。 しきい値は、テーブルが一時的か永続的かによって異なります。

    テーブルの種類 テーブルのカーディナリティ (n) 再コンパイルのしきい値 (# 変更数)
    Temporary n< 6 6
    Temporary 6 < = n<= 500 500
    Permanent n<= 500 500
    一時的または永続的 n> 500 500 + (0.20 * n)

    たとえば、テーブルに 20,000 行が含まれている場合、計算は 500 + (0.2 * 20,000) = 4,500 され、4,500 件の変更ごとに統計が更新されます。

  • SQL Server 2016 (13.x) 以降で、データベース互換性レベル が 130 の場合、データベース エンジンでは、統計情報の評価時点におけるテーブルのカーディナリティに応じて調整される、しきい値が低下する動的な統計再コンパイルしきい値が使用されます。 この変更により、大きなテーブルの統計がより頻繁に更新されます。 ただし、データベースの互換性レベルが 130 未満の場合は、SQL Server 2014 (12.x) のしきい値が適用されます。

    テーブルの種類 テーブルのカーディナリティ (n) 再コンパイルのしきい値 (# 変更数)
    Temporary n < 6 6
    Temporary 6 <= n <= 500 500
    Permanent n <= 500 500
    一時的または永続的 n > 500 MIN ( 500 + (0.20 * n), SQRT(1,000 * n) )

    たとえば、テーブルに 200 万行が含まれている場合、計算は 500 + (0.20 * 2,000,000) = 400,500SQRT(1,000 * 2,000,000) = 44,721の最小値になります。 つまり、統計は 44,721 回の変更ごとに更新されます。

Important

SQL Server 2008 R2 (10.50.x) から SQL Server 2014 (12.x)、またはデータベース互換レベル 120 以下のバージョンのある SQL Server 2016 (13.x) 以降のバージョンでは、トレース フラグ 2371 を有効にして、SQL Server で動的統計更新しきい値が減少するようにします。

すべてのシナリオで推奨されますが、トレース フラグ 2371 を有効にすることは省略可能です。 ただし、 SQL Server 2016 (13.x)より前の環境でトレース フラグ 2371 を有効にする場合、次のガイダンスを使用できます。

  • SAP システムを使用している場合は、このトレースを有効にします。 詳細については、こちらのトレース フラグ 2371 に関するブログを参照してください。
  • 現在の自動更新が十分な頻度でトリガーされないために、夜間ジョブに依存して統計を更新する必要がある場合は、トレース フラグ 2371 を有効にして、しきい値をテーブルカーディナリティに調整することを検討してください。

クエリ オプティマイザーによる古い統計の確認は、クエリをコンパイルする前と、キャッシュされたクエリ プランを実行する前に行われます。 クエリをコンパイルする前に、クエリ オプティマイザーはクエリ述語内の列、テーブル、およびインデックス付きビューを使用して、どの統計が古くなっている可能性があるかを判断します。 キャッシュされたクエリ プランを実行する前に、データベース エンジンはクエリ プランが -date 統計 up-to参照していることを確認します。

AUTO_UPDATE_STATISTICS オプションは、インデックス用に作成された統計オブジェクト、クエリ述語内の単一列、および CREATE STATISTICS ステートメントで作成された統計に適用されます。 また、フィルター選択された統計情報にも適用されます。

sys.dm_db_stats_propertiesを使用すると、テーブルで変更された行数を正確に追跡し、統計を手動で更新するかどうかを決定できます。

AUTO_UPDATE_STATISTICS はメモリ最適化テーブルでは常に OFF です。

AUTO_UPDATE_STATISTICS_ASYNC

統計の非同期更新オプション AUTO_UPDATE_STATISTICS_ASYNC によって、クエリ オプティマイザーで統計の同期更新と非同期更新のどちらを使用するかが決まります。 既定では、非同期統計更新オプションは OFFされ、クエリ オプティマイザーは統計を同期的に更新します。 AUTO_UPDATE_STATISTICS_ASYNC オプションは、インデックス用に作成された統計オブジェクト、クエリ述語内の単一列、および CREATE STATISTICS ステートメントで作成された統計に適用されます。

Note

SQL Server Management Studioで非同期統計更新オプションを設定するには、[データベースのプロパティ] ウィンドウの [オプション] ページで、[統計の自動更新] と [統計の自動更新] の両方を [非同期的に更新] を True に設定します。

統計の更新には、同期更新 (既定) と非同期更新があります。

  • 統計の同期更新では、クエリには常に最新の統計が使用され、コンパイルおよび実行されます。 統計が古い場合、クエリ オプティマイザーでは、統計が更新されるのを待機してからクエリがコンパイルされ、実行されます。

  • 統計の非同期更新では、既存の統計が古い場合でも、既存の統計を使用してクエリがコンパイルされます。 クエリ オプティマイザーは、クエリをコンパイルするときに統計が古い場合、最適ではないクエリ プランを選択する場合があります。 通常、統計はその後すぐに更新されます。 統計の更新が完了した後にコンパイルされるクエリは、更新された統計を使用する利点があります。

テーブルの切り捨てや大部分の行の一括更新を行うなど、データの分布が変わる操作を実行する場合は、同期統計を使用することを検討してください。 操作の完了後に統計を手動で更新しない場合は、同期統計を使用することで、クエリで必要になる前に統計が最新の状態になります。

次のような場合は、非同期統計を使用してクエリの応答時間を予測しやすくすることを検討してください。

  • アプリケーションで同じクエリ、類似のクエリ、またはキャッシュされた類似のクエリ プランを頻繁に実行する場合。 クエリの応答時間は、統計の同期更新を使用するよりも非同期更新を使用した方が予測しやすくなります。非同期更新の場合、クエリ オプティマイザーでは、統計が最新になるまで待機せずに着信クエリを実行できるためです。 これにより、一部のクエリの遅延については回避することができます。

  • 更新された統計を待機している 1 つ以上のクエリによって、アプリケーションでクライアント要求のタイムアウトが発生しました。 場合によっては、同期統計を待機すると、アグレッシブなタイムアウトのアプリケーションが失敗する可能性があります。

Note

ローカル一時テーブルの統計は、 AUTO_UPDATE_STATISTICS_ASYNC オプションに関係なく、常に同期的に更新されます。 グローバル一時テーブルの統計は、ユーザー データベースに設定された AUTO_UPDATE_STATISTICS_ASYNC オプションに従って同期または非同期で更新されます。

統計の非同期更新は、バックグラウンド要求によって実行されます。 要求は、更新された統計情報をデータベースに書き込む準備ができた時点で、統計メタデータ オブジェクトに対するスキーマ変更ロックの取得を試みます。 別のセッションが同じオブジェクトに対して既にロックを保持している場合、スキーマ変更ロックを取得できるようになるまで、非同期統計の更新がブロックされます。 同様に、クエリをコンパイルするために統計メタデータ オブジェクトに対するスキーマ安定性 (Sch-S) ロックを取得する必要があるセッションは、既にスキーマ変更ロックの取得を保持しているか待機している非同期統計更新のバックグラウンド セッションによってブロックされる可能性があります。 したがって、クエリのコンパイルや統計の更新が非常に頻繁に行われるワークロードでは、非同期統計を使用すると、ロックのブロックによる同時実行の問題が起きる可能性が高くなる場合があります。

Azure SQL Database、Azure SQL Managed Instance、および SQL Server 2022 (16.x) 以降では、ASYNC_STATS_UPDATE_WAIT_AT_LOW_PRIORITYdatabase スコープ構成を有効にした場合、非同期統計更新を使用してコンカレンシーの問題を回避できます。 この構成を有効にすると、バックグラウンド要求はスキーマ変更 (Sch-M) ロックの取得を待機し、更新された統計を別の優先順位の低いキューに保持し、他の要求が既存の統計を使用してクエリのコンパイルを続行できるようにします。 他のセッションが統計メタデータ オブジェクトのロックを保持していない場合、バックグラウンド要求はスキーマ変更ロックを取得し、統計を更新します。 万が一、バックグラウンド要求が数分以内にロックを取得できない場合、非同期統計の更新は中止され、統計は別の自動統計更新がトリガーされるまで、または統計が 手動で更新されるまで更新されません。

Note

ASYNC_STATS_UPDATE_WAIT_AT_LOW_PRIORITY データベース スコープ構成オプションは、Azure SQL Database、Azure SQL Managed Instance、および SQL Server 2022 (16.x) 以降のSQL Serverで使用できます。

AUTO_DROP オプション

適用対象: Azure SQL Database、Azure SQL Managed Instance、SQL Server 2022 (16.x) 以降

SQL Server 2022 (16.x) より前のSQL Serverでは、統計を手動で作成するか、ユーザー データベースでサード パーティ製ツールを使用すると、それらの統計オブジェクトがスキーマの変更をブロックしたり、妨げたりする可能性があります。

SQL Server 2022 (16.x) 以降の AUTO_DROP オプションは、新しいデータベースと移行されたすべてのデータベースで既定で有効になります。 AUTO_DROP プロパティを有効にすると、統計オブジェクトが作成され、以降のスキーマ変更は統計オブジェクトによってブロックされず、代わりに必要に応じて統計が削除されます。 このように、自動削除が有効になっている手動で作成された統計は、自動作成された統計と同様に動作します。

Azure SQL Database、Azure SQL Managed Instance、および SQL Server 2022 (16.x) 以降のバージョンでは、自動的に作成された統計は、AUTO_DROPが有効になっているかのように常に動作します。

Note

自動作成された統計で自動削除プロパティを設定または設定解除しようとすると、エラーが発生する可能性があります。 自動作成された統計では、常に自動削除が使用されます。 一部のバックアップでは、復元時に、統計オブジェクトが次回更新 (手動または自動) されるまで、このプロパティが正しく設定されないことがあります。 ただし、自動作成された統計は、自動削除の統計と同様に動作します。 以前のバージョンから SQL Server 2022 (16.x) にデータベースを復元する場合は、データベースに対して sp_updatestats を実行し、統計自動削除機能の適切なメタデータを設定することをお勧めします。

たとえば、dbo.DatabaseLogテーブルに統計オブジェクトを手動で作成するには、次のようにします。

CREATE STATISTICS [mystats]
    ON [dbo].[DatabaseLog]([DatabaseLogID], [PostTime], [DatabaseUser])
    WITH AUTO_DROP = ON;

たとえば、dbo.DatabaseLogテーブルの統計オブジェクトに対する自動削除設定を更新するには、次のようにしますす。

UPDATE STATISTICS [dbo].[DatabaseLog] ([mystats])
    WITH AUTO_DROP = ON;

既存の統計の自動削除設定を評価するには、auto_dropsys.stats 列を使用します。

SELECT object_id,
       [name],
       auto_drop
FROM sys.stats;

詳細については、AUTO_DROPを参照してください。

INCREMENTAL

適用対象: SQL Server 2014 (12.x) 以降のバージョン。

INCREMENTALCREATE STATISTICS オプションを ON に設定すると、パーティションごとの統計情報が作成されます。 OFFに設定すると、データベースは統計ツリーを削除し、統計を再計算します。 既定値は OFF です。 この設定は、データベース レベルの INCREMENTAL プロパティをオーバーライドします。

大きなテーブルに新しいパーティションを追加する場合は、新しいパーティションを含むように統計を更新する必要があります。 ただし、テーブル全体 (FULLSCAN または SAMPLE オプション) のスキャンに必要な時間は長い場合があります。 また、新しいパーティションに対する統計のみが必要となるため、テーブル全体をスキャンする必要はありません。 増分オプションは、パーティションごとに統計を作成して格納します。更新すると、新しい統計が必要なパーティションの統計のみが更新されます。

パーティションごとの統計がサポートされていない場合、データベースはこのオプションを無視し、警告を生成します。 増分統計は、次の統計の種類ではサポートされていません。

  • ベース テーブルとパーティションアラインされていないインデックスを使用して作成された統計。
  • Always On の読み取り可能なセカンダリ データベースに対して作成された統計。
  • 読み取り専用のデータベースに対して作成された統計。
  • フィルター選択されたインデックスに対して作成された統計。
  • ビューに対して作成された統計。
  • 内部テーブルに対して作成された統計。
  • 空間インデックスまたは XML インデックスを使用して作成された統計。

統計を作成する場合

クエリ オプティマイザーによって、既に次のようにして統計が作成されています。

  1. テーブルまたはビューにインデックスを作成すると、クエリ オプティマイザーによってインデックスの統計が作成されます。 これらの統計は、インデックスのキー列について作成されます。 インデックスがフィルター選択されたインデックスの場合は、フィルター選択されたインデックスに指定された行のサブセットと同じ行のサブセットについて、フィルター選択された統計が作成されます。 フィルター選択されたインデックスの詳細については、「」を参照してください。

    Note

    SQL Server 2014 (12.x) 以降のバージョンでは、パーティション インデックスを作成または再構築するときにテーブル内のすべての行をスキャンして統計を作成するわけではありません。 代わりに、クエリ オプティマイザーが既定のサンプリング アルゴリズムを使用して統計を生成します。 パーティション インデックスでデータベースをアップグレードした後で、これらのインデックスのヒストグラム データに違いが見つかる場合があります。 この動作の変更はクエリ パフォーマンスに影響しない可能性があります。 テーブル内のすべての行をスキャンしてパーティション分割されたインデックスの統計を取得するには、CREATE STATISTICS 句と共に UPDATE STATISTICS または FULLSCAN を使用します。

  2. AUTO_CREATE_STATISTICS がオンの場合、クエリ オプティマイザーによってクエリ述語内の列に対して 1 列ずつ統計が作成されます。

ほとんどのクエリでは、統計を作成するためのこれら 2 つの方法によって、高品質のクエリ プランが保証されます。 場合によっては、 CREATE STATISTICS ステートメントを使用して追加の統計を作成することで、クエリ プランを改善できます。 これらの追加の統計では、クエリ オプティマイザーがインデックスまたは単一列の統計を作成するときに考慮しない統計的相関関係をキャプチャできます。 アプリケーションのテーブル データには、計算して統計オブジェクトに含めればクエリ オプティマイザーでクエリ プランを向上させることができる、他の統計的相関関係が含まれている場合があります。 たとえば、データ行のサブセットに関するフィルター選択された統計情報や、クエリ述語列の複数列統計を使用することで、クエリ プランが向上することがあります。

CREATE STATISTICS ステートメントを使用して統計を作成する場合は、クエリ オプティマイザーがクエリ述語列の単一列統計を引き続き定期的に作成できるように、AUTO_CREATE_STATISTICS オプションをONのままにします。 クエリ述語の詳細については、「検索条件の」を参照してください。

次のいずれかの条件が適用される場合は、 CREATE STATISTICS ステートメントを使用して統計を作成することを検討してください。

  • データベース エンジン チューニング アドバイザーによって統計の作成が提案されます。
  • クエリ述語には、同じインデックスにまだキーではない複数の相関列が含まれています。
  • データのサブセットから選択するクエリを使用する。
  • クエリに統計がない。

Note

メモリ内 OLTP 関連のテーブルと統計に関する詳細については、「メモリ最適化テーブルの統計」を参照してください。

クエリ述語に複数の相関列が含まれている

列間に相関関係や依存関係がある複数の列がクエリ述語に含まれている場合、複数列の統計を使用するとクエリ プランが向上することがあります。 複数の列の統計には、単一列の統計では使用できない 密度と呼ばれるクロス列相関統計が含まれています。 複数の列間のデータの相関関係によってクエリ結果が異なる場合、密度を使用するとカーディナリティの推定が向上します。

列が既に同じインデックスにある場合は、複数列の統計オブジェクトが既に存在するため、手動で作成する必要はありません。 列がまだ同じインデックスにない場合は、列にインデックスを作成するか、 CREATE STATISTICS ステートメントを使用して、複数列の統計を作成できます。 メンテナンスに必要なシステム リソースは、インデックスの方が統計オブジェクトよりも多くなります。 アプリケーションで複数列インデックスが必要ない場合は、インデックスを作成せずに統計オブジェクトを作成することで、システム リソースを節約できます。

複数列統計を作成する場合、統計オブジェクト定義の列の順序は、カーディナリティ推定を行う密度の有効性に影響します。 統計オブジェクトには、統計オブジェクト定義内のキー列の各プレフィックスの密度が格納されます。 密度の詳細については、この記事の 「密度 」セクションを参照してください。

カーディナリティの推定に効果的な密度を作成するには、クエリ述語内の列が、統計オブジェクト定義内の列のいずれかのプレフィックスに一致する必要があります。 たとえば、次の例では、列 LastNameMiddleNameFirstName で複数列統計オブジェクトが作成されます。

USE AdventureWorks2022;
GO

IF EXISTS (SELECT name
           FROM sys.stats
           WHERE name = 'LastFirst'
                 AND object_ID = OBJECT_ID('Person.Person'))
    DROP STATISTICS Person.Person.LastFirst;
GO

CREATE STATISTICS LastFirst
    ON Person.Person(LastName, MiddleName, FirstName);
GO

この例では、統計オブジェクト LastFirst に、列プレフィックス ((LastName))、((LastName, MiddleName))、および ((LastName, MiddleName, FirstName)) の密度が格納されています。 密度は、(LastName, FirstName)では使用できません。 クエリで LastNameを使用せずに FirstNameMiddleName を使用する場合、密度はカーディナリティ推定では使用できません。

データのサブセットからクエリが選択される

クエリ オプティマイザーでは、1 列ずつおよびインデックスに対して統計を作成する際、すべての行の値に対する統計を作成します。 行のサブセットから選択するクエリの場合、その行のサブセットのデータ分布が一意であれば、フィルター選択された統計情報を使用することでクエリ プランを向上させることができます。 フィルター処理された統計を作成するには、CREATE STATISTICS 句で ステートメントを使用して、フィルター述語式を定義します。

たとえば、AdventureWorks2025 を使用すると、 Production.Product テーブル内の各製品は、 Production.ProductCategory テーブルの 4 つのカテゴリ ( BikesComponentsClothingAccessories) のいずれかに属します。 各カテゴリでは、重量に関するデータ分布が異なります。自転車の重量は 13.77 ~ 30.0、部品の重量は 2.12 ~ 1050.00 (一部 NULL 値)、衣類の重量はすべて NULL、付属品の重量も NULL です。

例として Bikes を使用すると、すべての自転車の重みに対してフィルター処理された統計情報により、クエリ オプティマイザーにより正確な統計が提供され、完全テーブル統計または [重み] 列に存在しない統計と比較してクエリ プランの品質が向上します。 自転車の重量列は、統計情報のフィルターには適していますが、重量による検索回数が比較的少ない場合は、フィルター インデックスに必ずしも適しているとは限りません。 フィルター選択されたインデックスを使用することで得られる参照のパフォーマンスの向上よりも、フィルター選択されたインデックスをデータベースに追加するためのメンテナンス コストとストレージ コストの増加の方が大きい場合があります。

次のステートメントでは、BikeWeightsのすべてのサブカテゴリに対して、フィルター処理された Bikes 統計情報が作成されます。 フィルター選択された述語式で、比較 Production.ProductSubcategoryID IN (1,2,3)を使用して自転車のすべてのサブカテゴリを列挙することで、自転車を定義しています。 述語は、Bikes テーブルに格納され、フィルター式のすべての列が同じテーブル内にある必要があるため、Production.ProductCategory カテゴリ名を使用できません。

USE AdventureWorks2022;
GO
IF EXISTS ( SELECT name FROM sys.stats
    WHERE name = 'BikeWeights'
    AND object_ID = OBJECT_ID ('Production.Product'))
DROP STATISTICS Production.Product.BikeWeights;
GO
CREATE STATISTICS BikeWeights
    ON Production.Product (Weight)
WHERE ProductSubcategoryID IN (1,2,3);
GO

クエリ オプティマイザーでは、 BikeWeights というフィルター選択された統計情報を使用して、重量が 25を超えるすべての自転車を選択する次のクエリのクエリ プランを向上させることができます。

SELECT P.Weight AS Weight,
       S.Name AS BikeName
FROM Production.Product AS P
     INNER JOIN Production.ProductSubcategory AS S
         ON P.ProductSubcategoryID = S.ProductSubcategoryID
WHERE P.ProductSubcategoryID IN (1, 2, 3)
      AND P.Weight > 25
ORDER BY P.Weight;
GO

統計がないことをクエリで識別する

クエリ オプティマイザーでは、エラーやその他のイベントによって統計を作成できない場合、統計を使用せずにクエリ プランを作成します。 クエリ オプティマイザーでは存在しない統計をマークし、次回のクエリの実行時に再生成しようとします。

統計が存在しない場合は、SQL Server Management Studio を使用してクエリの実行プランをグラフィカルに表示すると、警告 (赤色のテーブル名) が表示されます。 また、SQL Server Profiler を使用して Missing Column Statistics イベント クラスを監視すると、統計がない場合はそのことがわかります。 詳細については、「Errors and Warnings イベント カテゴリ (データベース エンジン)」を参照してください。

統計がない場合は、次の手順を実行します。

  • AUTO_CREATE_STATISTICSAUTO_UPDATE_STATISTICS が ON になっていることを確認します。
  • データベースが読み取り専用ではないことを確認します。 データベースが読み取り専用の場合、新しい統計オブジェクトを保存できません。
  • CREATE STATISTICS ステートメントを使用して、不足している統計を作成します。

一時的な統計

読み取り専用データベースまたは読み取り専用スナップショットに関する統計が欠落しているか、古くなっている場合、データベース エンジンは、tempdbに一時的な統計を作成して維持します。 データベース エンジンが一時的な統計を作成する場合、一時的な統計と永続的な統計とを区別するためのサフィックス _readonly_database_statistic が統計名に付加されます。 サフィックス _readonly_database_statisticは、データベース エンジンによって生成された統計用に予約されています。 一時統計のスクリプトは、読み取り/書き込みデータベースで作成および実行できます。 スクリプトを作成する場合 Management Studioでは、統計名のサフィックスを _readonly_database_statistic から _readonly_database_statistic_scripted に変更します。

一時的な統計を作成および更新できるのは、データベース エンジンだけです。 ただし、永続的な統計の場合と同じツールを使用すると、一時的な統計を削除して、統計のプロパティを監視できます。

  • DROP STATISTICS ステートメントを使用して一時的な統計を削除します。
  • sys.stats カタログ ビューと sys.stats_columns カタログ ビューを使用して統計を監視します。 sys.stats システム カタログ ビューには、どの統計が一時的または永続的なものかを示すための is_temporary 列が含まれています。

一時的な統計は tempdbに格納されるため、データベース エンジンを再起動すると、一時的な統計がすべて削除されます。

すべての統計と同様に、一時的な統計を作成および更新するには、オブジェクトのスキーマ変更 (Sch-M) ロックが必要です。 このロックにより、プライマリ レプリカからのトランザクションを適用するセカンダリ レプリカのシステム再実行プロセスなど、他のクエリやプロセスがブロックされる可能性があります。 このブロックがクエリワークロードまたはデータ伝達に影響する場合は、 READABLE_SECONDARY_TEMPORARY_STATS_AUTO_CREATEREADABLE_SECONDARY_TEMPORARY_STATS_AUTO_UPDATEdatabase スコープの構成 をそれぞれ使用して、一時的な統計の自動作成と更新を無効にすることができます。

統計を更新する場合

クエリ オプティマイザーは、統計が古い可能性があるタイミングを決定し、クエリ プランに必要なときに更新します。 場合によっては、AUTO_UPDATE_STATISTICSがされている場合よりも統計を頻繁に更新することで、クエリ プランON改善し、クエリのパフォーマンスを向上させることができます。 統計は、 UPDATE STATISTICS ステートメントまたはストアド プロシージャ sp_updatestatsを使用して更新できます。

統計を更新すると、クエリが最新の統計を使用してコンパイルされるようになります。 任意のプロセスで統計を更新すると、クエリ プランが自動的に再コンパイルされる可能性があります。 クエリ プランの改善とクエリの再コンパイルにかかる時間の間にパフォーマンスのトレードオフがあるため、統計を手動で頻繁に更新しないでください。 実際のトレードオフはアプリケーションによって異なります。

UPDATE STATISTICSまたはsp_updatestatsを使用して統計を更新する場合は、クエリ オプティマイザーAUTO_UPDATE_STATISTICS定期的に統計を更新できるように、ONに設定したままにします。

  • 列、インデックス、テーブル、またはインデックス付きビューの統計を更新する方法の詳細については、 UPDATE STATISTICSを参照してください。

  • データベース内のすべてのユーザー定義テーブルと内部テーブルの統計を更新する方法については、ストアド プロシージャの sp_updatestatsを参照してください。

  • 統計の自動更新のしきい値に関する詳細については、「AUTO_UPDATE_STATISTICS オプション」を参照してください。

AUTO_UPDATE_STATISTICSOFFに設定した場合、プランの再コンパイルは他のさまざまな理由で引き続き発生する可能性がありますが、古い統計の更新が原因で自動的に行われることはありません。 AUTO_UPDATE_STATISTICSOFFに設定した場合、統計の更新は、メンテナンス プランなど、手動でスケジュールされた他のプロセスでのみ行われます。 そのため、 AUTO_UPDATE_STATISTICSOFF に設定すると、最適ではないクエリ プランが発生し、クエリ パフォーマンスが低下する可能性があります。

古くなった統計の検出

統計の最終更新日を確認するには、sys.dm_db_stats_properties または STATS_DATE 関数を使用します。

次のような場合は、統計を更新することを検討してください。

  • クエリの実行に時間がかかる。
  • 昇順または降順のキー列に対して挿入操作を実行する。
  • メンテナンス操作の実行後。

統計を手動で更新する例については、 UPDATE STATISTICSを参照してください。

クエリの実行に時間がかかる

クエリの応答時間が遅い場合や予測できない場合は、他のトラブルシューティング手順を実行する前に、クエリの統計が最新のものであることを確認してください。

昇順または降順のキー列に対して挿入操作を実行する

IDENTITYやリアルタイムのタイムスタンプ列など、昇順または降順のキー列の統計では、クエリ オプティマイザーが実行するよりも頻繁に統計を更新する必要があります。 挿入操作によって昇順または降順の列に新しい値が追加された場合に、 追加された行数が少なすぎると、統計の更新が実行されないことがあります。 統計が -date up-toではなく、最近追加された行からクエリが選択されている場合、現在の統計には、これらの新しい値のカーディナリティの推定値はありません。 この条件により、カーディナリティの推定が不正確になり、クエリのパフォーマンスが低下する可能性があります。

たとえば、最新の販売注文の日付から選択するクエリでは、最新の販売注文日のカーディナリティ見積もりを含むように統計が更新されない場合、カーディナリティの見積もりが不正確になります。

メンテナンス操作の実行後

テーブルの切り捨てや大部分の行の一括挿入を行うなど、データの分布が変わるメンテナンス操作を実行した後は、統計を更新することを検討してください。 統計を事前に更新すると、クエリが統計の自動更新を待機している間に、クエリ処理の将来の遅延を回避できます。

インデックスの再構築、最適化、再構成などの操作では、データの分散は変更されません。 そのため、REBUILD、ALTER INDEX、DBCC INDEXDEFRAG、または REORGANIZE 操作実行した後に統計ALTER INDEX更新する必要はありません。 ALTER INDEX REBUILD または DBCC DBREINDEX を使用してテーブルまたはビューのインデックスを再構築した場合、クエリ オプティマイザーによって統計が更新されますが、この統計の更新はインデックスを再作成する過程で実行されるものです。 クエリ オプティマイザーは、DBCC INDEXDEFRAG または ALTER INDEX REORGANIZE 操作後に統計を更新しません。

Tip

SQL Server 2016 (13.x) SP1 CU4 以降では、CREATE STATISTICSまたはUPDATE STATISTICSのPERSIST_SAMPLE_PERCENT オプションを使用して、サンプリング率を明示的に指定しない後続の統計更新の特定のサンプリング率を設定して保持します。

インデックスと統計の自動管理

Adaptive Index Defrag のような賢いソリューションを活用し、1 個以上のデータベースに対するインデックスの最適化と統計更新を自動管理します。 この手順では、断片化レベルに従ってインデックスを再構築または再構成するかどうかを自動的に選択し、線形しきい値を使用して統計を更新します。

クエリ オプティマイザーが使用した統計を決定する

クエリ オプティマイザーがクエリをコンパイルするときに使用する統計オブジェクトは、推定実行プランまたは実際の 実行プランを調べることで確認できます。 実行プランを調べると、 OptimizerStatsUage 要素には、コンパイル時にクエリ オプティマイザーによって読み込まれた統計オブジェクトに関する情報を含む StatisticsInfo 要素が含まれます。 StatisticsInfo要素には、統計オブジェクト名、それが属するデータベース、スキーマ、およびテーブル、コンパイル時の変更数、サンプリング率、および最終更新日時が含まれます。

実行プランを調べるには、次のいずれかの手法を使用します。

  • SQL Server Management Studioで、クエリを実行する前に [実際の実行プランを含める] (Ctrl + M) を選択します。 結果と共に表示される [実行プラン ] タブでは、次のことができます。
    • グラフィカル プラン内を右クリックし、[ 実行プラン XML の表示] を選択します。 OptimizerStatsUsage要素と各子StatisticsInfo要素を探します。
    • 最後の (左端) 演算子を選択します。 ( SELECT クエリの場合、この演算子は SELECT ノードです)。 [プロパティ ] ウィンドウで、 OptimizerStatsUsage ノードを展開し、クエリで使用される統計オブジェクトに関する情報を表示します。
  • クエリの前に SETSET STATISTICS XML ON を実行します。 結果と共に表示されるハイパーリンクを選択して、実行プラン XML を表示します。
  • 最近のクエリについては、sys.dm_exec_query_plan または sys.dm_exec_query_statistics_xml を照会します。
  • sys.query_store_planを使用して、クエリ ストアから以前にキャプチャしたプランを読み取

StatisticsInfo 要素は、 AdventureWorks2022 サンプル データベースのクエリの次の XML フラグメントのようになります。

<StatisticsInfo 
    Database="[AdventureWorks2022]" 
    Schema="[Sales]" 
    Table="[SalesOrderDetail]" 
    Statistics="[IX_SalesOrderDetail_ProductID]" 
    ModificationCount="0" 
    SamplingPercent="100" 
    LastUpdate="2025-09-07T15:32:16.89" />
Attribute Meaning
DatabaseSchemaTable 統計が属するオブジェクト。
Statistics データベース内の統計オブジェクトの名前。 この名前を DBCC SHOW_STATISTICS または sys.stats と共に使用して、ヒストグラムと密度ベクトルを調べます。
ModificationCount 統計が最後に更新されてからの、プランのコンパイル時のデータ変更の数。 テーブル サイズを基準とした大きな値は、コンパイル中に統計が古くなったことを示します。
SamplingPercent 統計を作成するためにサンプリングされた行の割合。 値を小さくすると、歪んだデータのヒストグラムの精度が低下する可能性があります。
LastUpdate 前回の統計更新のタイムスタンプ。 データベースで AUTO_UPDATE_STATISTICS オプションが有効になっている場合、データベースは必要に応じて統計を自動的に更新します。

Note

StatisticsInfo は、プランのコンパイル中に使用可能で考慮された統計を反映します。 クエリがフィルター処理する列に対して StatisticsInfo エントリが見つからない場合、クエリ オプティマイザーは関連する統計を識別しませんでした。これは、パフォーマンスが低下する可能性があります。

統計オブジェクトの現在の鮮度と変更数を確認するには、 sys.dm_db_stats_propertiesを使用します。 たとえば、次のクエリでは、テーブル IX_SalesOrderDetail_ProductIDSales.SalesOrderDetailという名前の統計オブジェクトの現在のメトリックが提供されます。

SELECT
    OBJECT_SCHEMA_NAME(s.object_id) AS schema_name,
    OBJECT_NAME(s.object_id)        AS table_name,
    s.name                          AS statistics_name,
    sp.last_updated,
    sp.rows,
    sp.rows_sampled,
    sp.modification_counter
FROM sys.stats AS s
OUTER APPLY sys.dm_db_stats_properties(s.object_id, s.stats_id) AS sp
WHERE s.object_id = OBJECT_ID(N'Sales.SalesOrderDetail')
  AND s.name = N'IX_SalesOrderDetail_ProductID';

現在のデータベースの 統計の自動作成と更新のオプション が有効になっているかどうかを確認するには、次の値を使用します。

SELECT [name],
    is_auto_create_stats_on,
    is_auto_update_stats_on,
    is_auto_update_stats_async_on
FROM sys.databases
WHERE [name] = DB_NAME();

統計を効果的に使用するクエリ

クエリ述語にローカル変数や複雑な式が含まれている場合など、特定のクエリ実装では、最適なクエリ プランにならないことがあります。 これらの問題を回避するには、統計を効果的に使用するためのクエリ設計ガイドラインに従ってください。 クエリ述語の詳細については、「検索条件の」を参照してください。

クエリのデザイン ガイドラインを適用して統計を効果的に使用することで、クエリ述語で使用される式、変数、および関数に対する カーディナリティの推定 を向上させると、クエリ プランを向上させることができます。 クエリ オプティマイザーが式、変数、または関数の値を認識しない場合、ヒストグラムで検索する値が認識されないため、ヒストグラムから最適なカーディナリティ推定を取得できません。 その場合、クエリ オプティマイザーでは、ヒストグラム内のサンプリングされたすべての行の値ごとの平均行数に基づいてカーディナリティの推定を行います。 この状況により、最適でないカーディナリティの推定が発生し、クエリのパフォーマンスが低下する可能性があります。 ヒストグラムの詳細については、この記事またはsys.dm_db_stats_histogramヒストグラムのセクションを参照してください。

以下のガイドラインでは、カーディナリティの推定を向上させることによってクエリ プランを改善するためのクエリの作成方法について説明します。

式に対するカーディナリティの推定を向上させる

式に対するカーディナリティの推定を向上させるには、次のガイドラインに従います。

  • 可能な限り、定数を含む式を簡略化します。 クエリ オプティマイザーでは、カーディナリティの推定を決定する前に、定数を含むすべての関数と式が評価されるわけではありません。 たとえば、式 ABS(-100)100 に簡略化します。
  • 式で複数の変数を使用している場合は、式の計算列を作成し、その計算列に対する統計またはインデックスを作成することを検討します。 たとえば、クエリ述語 WHERE PRICE + Tax > 100 のカーディナリティの推定は、式 Price + Tax に対する計算列を作成すると向上する可能性があります。

変数および関数に対するカーディナリティの推定を向上させる

変数と関数のカーディナリティ推定を改善するには、次のガイドラインに従います。

  • クエリ述語でローカル変数を使用している場合は、ローカル変数の代わりにパラメーターを使用してクエリを書き換えることを検討します。 クエリ オプティマイザーは、クエリ実行プランの作成時にローカル変数の値を認識しません。 クエリでパラメーターを使用する場合、クエリ オプティマイザーは、ストアド プロシージャが受け取る最初の実際のパラメーター値のカーディナリティ推定を使用します。

  • 標準テーブルまたは一時テーブルを使用して、複数ステートメントのテーブル値関数の結果を保持することを検討してください。 クエリ オプティマイザーでは、複数ステートメントのテーブル値関数の統計は作成されません。 この方法を使用すると、クエリ オプティマイザーはテーブル列に統計を作成し、それらを使用してより優れたクエリ プランを作成できます。

  • テーブル変数の代わりに標準のテーブルか一時テーブルを使用することを検討します。 クエリ オプティマイザーでは、テーブル変数の統計は作成されません。 この方法を使用すると、クエリ オプティマイザーはテーブル列に統計を作成し、それらを使用してより優れたクエリ プランを作成できます。 一時テーブルとテーブル変数のどちらを使用するかを決定する際にトレードオフがあります。 ストアド プロシージャで使用されるテーブル変数は、一時テーブルよりもストアド プロシージャの再コンパイルが少なくなります。 アプリケーションによっては、テーブル変数の代わりに一時テーブルを使用しても、パフォーマンスが向上しない場合もあります。

  • 渡されたパラメーターを使用するクエリがストアド プロシージャに含まれている場合は、パラメーター値がクエリで使用される前にストアド プロシージャ内で変更されないようにします。 クエリに対するカーディナリティの推定は、更新された値ではなく渡されたパラメーターの値に基づいて行われます。 パラメーター値が変更されないようにするには、2 つのストアド プロシージャを使用するようにクエリを書き換えます。

    たとえば、次のストアド プロシージャ Sales.GetRecentSales では、@date@date の場合にパラメーター NULL の値を変更します。

    USE AdventureWorks2022;
    GO
    
    IF OBJECT_ID('Sales.GetRecentSales', 'P') IS NOT NULL
        DROP PROCEDURE Sales.GetRecentSales;
    GO
    
    CREATE PROCEDURE Sales.GetRecentSales
    @date DATETIME
    AS
    BEGIN
        IF @date IS NULL
            SET @date = DATEADD(MONTH, -3,
                (SELECT MAX(ORDERDATE)
                FROM Sales.SalesOrderHeader));
        SELECT *
        FROM Sales.SalesOrderHeader AS h, Sales.SalesOrderDetail AS d
        WHERE h.SalesOrderID = d.SalesOrderID
            AND h.OrderDate > @date;
    END
    GO
    

    ストアド プロシージャの最初の呼び出し Sales.GetRecentSalesNULL パラメーターの @date を渡した場合、クエリ述語が @date = NULLで呼び出されていない場合でも、クエリ オプティマイザーは @date = NULL のカーディナリティ推定を使用してストアド プロシージャをコンパイルします。 このカーディナリティの見積もりは、実際のクエリ結果の行数と大きく異なる場合があります。 そのため、クエリ オプティマイザーにより、最適なクエリ プランが選択されないことがあります。 この問題を回避するために、ストアド プロシージャを次の 2 つのプロシージャに書き換えることができます。

    USE AdventureWorks2022;
    GO
    
    IF OBJECT_ID('Sales.GetNullRecentSales', 'P') IS NOT NULL
        DROP PROCEDURE Sales.GetNullRecentSales;
    GO
    
    CREATE PROCEDURE Sales.GetNullRecentSales
    @date DATETIME
    AS
    BEGIN
        IF @date IS NULL
            SET @date = DATEADD(MONTH, -3,
                (SELECT MAX(ORDERDATE)
                FROM Sales.SalesOrderHeader));
        EXECUTE Sales.GetNonNullRecentSales @date;
    END
    GO
    
    IF OBJECT_ID('Sales.GetNonNullRecentSales', 'P') IS NOT NULL
        DROP PROCEDURE Sales.GetNonNullRecentSales;
    GO
    
    CREATE PROCEDURE Sales.GetNonNullRecentSales
    @date DATETIME
    AS
    BEGIN
        SELECT *
        FROM Sales.SalesOrderHeader AS h, Sales.SalesOrderDetail AS d
        WHERE h.SalesOrderID = d.SalesOrderID
            AND h.OrderDate > @date;
    END
    GO
    

クエリ ヒントを使用してカーディナリティの推定を向上させる

ローカル変数のカーディナリティ推定を向上させるには、 OPTIMIZE FOR <value> または OPTIMIZE FOR UNKNOWN クエリ ヒントと RECOMPILEを使用します。 詳細については、クエリ ヒントを参照してください。

アプリケーションによっては、クエリを実行するたびに再コンパイルすると時間がかかりすぎる場合がありますが、 OPTIMIZE FOR クエリ ヒントは RECOMPILE オプションを使用しなくても役立つことがあります。 たとえば、ストアド プロシージャ OPTIMIZE FORSales.GetRecentSales オプションを追加して、特定の日付を指定することができます。 OPTIMIZE FOR プロシージャに Sales.GetRecentSales を追加した例を次に示します。

USE AdventureWorks2022;
GO

IF OBJECT_ID('Sales.GetRecentSales', 'P') IS NOT NULL
    DROP PROCEDURE Sales.GetRecentSales;
GO

CREATE PROCEDURE Sales.GetRecentSales
@date DATETIME
AS
BEGIN
    IF @date IS NULL
        SET @date = DATEADD(MONTH, -3,
            (SELECT MAX(ORDERDATE)
            FROM Sales.SalesOrderHeader));
    SELECT *
    FROM Sales.SalesOrderHeader AS h, Sales.SalesOrderDetail AS d
    WHERE h.SalesOrderID = d.SalesOrderID AND h.OrderDate > @date
    OPTION (OPTIMIZE FOR (@date = '2004-05-01 00:00:00.000'));
END
GO

プラン ガイドを使用してカーディナリティの推定を向上させる

一部のアプリケーションでは、クエリを変更できないか、RECOMPILE クエリ ヒントによって再コンパイルが多すぎる可能性があるため、クエリの設計ガイドラインが適用されない場合があります。 プラン ガイドを使用して、 USE PLANなどの他のヒントを指定して、アプリケーションベンダーとのアプリケーションの変更を調査しながらクエリの動作を制御します。 プラン ガイドの詳細については、「 Plan Guides」を参照してください。

Azure SQL Database では、プラン ガイドではなく、プランを強制するクエリ ストアのヒントを検討してください。 詳細については、「クエリ ストアのヒント」を参照してください。