FORCESEEK テーブル ヒントの使用

FORCESEEK テーブル ヒントを使用すると、クエリで参照されているテーブルやビューのデータへのアクセス パスとしてインデックスのシーク操作のみを使用するようにクエリ オプティマイザを設定できます。これにより、クエリ オプティマイザによって選択された既定のプランをオーバーライドして、非効率なクエリ プランによって引き起こされるパフォーマンス上の問題を回避することができます。たとえば、プランにテーブル スキャン オペレータやインデックス スキャン オペレータが含まれている場合に、対応するテーブルでクエリの実行中に多数の読み取りが行われることが STATISTICS IO の出力からわかった場合は、インデックスのシーク操作を使用するように設定することでクエリ パフォーマンスを向上させることができます。これは、基数やコストの不正確な推定のためにプランのコンパイル時にオプティマイザでスキャン操作が優先された場合には特に効果的です。

FORCESEEK は、クラスタ化インデックスのシーク操作と非クラスタ化インデックスのシーク操作の両方に適用されます。SELECT ステートメントの FROM 句や、UPDATE ステートメントや DELETE ステートメントの FROM <table_source> 句の任意のテーブルやビューに対して指定できます。

注記注意

通常、SQL Server クエリ オプティマイザでは、クエリにとって最適な実行プランが選択されるため、ヒントは、経験を積んだ開発者やデータベース管理者が最後の手段としてのみ使用することをお勧めします。

クエリ プランの FORCESEEK の適用性の評価

FORCESEEK テーブル ヒントは、インデックスのシーク操作の方が効率的なのにクエリ プランでテーブルやビューに対してテーブル スキャン オペレータやインデックス スキャン オペレータが使用されている場合に使用できます。ここでは、次のクエリと、その後の実行プランについて考えてみます。

USE AdventureWorks2008R2;
GO
SELECT *
FROM Sales.SalesOrderHeader AS h
INNER JOIN Sales.SalesOrderDetail AS d 
    ON h.SalesOrderID = d.SalesOrderID 
WHERE h.TotalDue > 100
AND (d.OrderQty > 5 OR d.LineTotal < 1000.00);
GO

次の実行プランは、クエリ オプティマイザが両方のテーブルのデータへのアクセスにクラスタ化インデックス スキャン オペレータを選択したことを示しています。

Clustered Index Scan 操作での実行プラン

Sales.SalesOrderDetail テーブルに対してシーク操作を実行するようにクエリ オプティマイザを設定するには、次のクエリのように FORCESEEK ヒントを指定します。

USE AdventureWorks2008R2;
GO
SELECT *
FROM Sales.SalesOrderHeader AS h
INNER JOIN Sales.SalesOrderDetail AS d WITH (FORCESEEK)
    ON h.SalesOrderID = d.SalesOrderID 
WHERE h.TotalDue > 100
AND (d.OrderQty > 5 OR d.LineTotal < 1000.00);
GO

次の実行プランは、クエリで FORCESEEK ヒントを使用した結果を示しています。Sales.SalesOrderDetail テーブルのデータへのアクセスにクラスタ化インデックスのシーク操作が使用されています。

Clustered Index Seek 操作での実行プラン

インデックス和集合とインデックス積集合のサポート

FORCESEEK ヒントは、インデックス和集合とインデックス積集合をサポートしています。FORCESEEK ヒントを使用すると、クエリ オプティマイザでこれらの技法が使用される可能性が高くなります。インデックス和集合とインデックス積集合は、単純なクエリでコンパイル時間が増加するのを避けるため、通常は、列の基数と選択度を考慮する規則に従ってのみ選択されます。しかし、FORCESEEK ヒントを指定すると、それらの規則が無視されて、常にこれらの手法が考慮されるようになります。たとえば、次のクエリについて考えてみます。

SELECT * FROM T WITH(FORCESEEK) WHERE T.a = 1 AND T.b = 2;

SELECT * FROM T WITH(FORCESEEK) WHERE T.a = 1 AND T.b = 2;

テーブル T の列 a および b に個別の非クラスタ化インデックスがある場合は、インデックス積集合プランが選択される可能性があります。このプランでは、列 a の非クラスタ化インデックスのシーク操作と列 b の非クラスタ化インデックスのシーク操作が含まれ、ベース テーブルへの参照操作が実行される前に結果のインデックス キー セットの共通部分が取得されます。

次の例では、インデックス和集合プランが選択されます。このプランでは、列 a のシーク操作と列 b のシーク操作が含まれ、ベース テーブルへの参照操作が実行される前に結果のインデックス キー セットの和集合が取得されます。

SELECT * FROM T WITH(FORCESEEK) WHERE T.a = 1 OR T.b = 2;

SELECT * FROM T WITH(FORCESEEK) WHERE T.a = 1 OR T.b = 2;

LIKE や IN を使用するクエリでの FORCESEEK の使用

クエリ オプティマイザの規則や不正確な基数の推定のために、クエリで検索述語として IN や LIKE が使用されている場合に、オプティマイザでインデックスのシークではなくテーブル スキャン操作やインデックス スキャン操作が実行される可能性もあります。

次の例は、FORCESEEK ヒントを使用して、検索述語として LIKE や IN が使用されている場合にテーブル スキャンではなくインデックス シーク操作を実行するようにクエリ オプティマイザを設定する方法を示しています。クエリ実行プランを表示するには、この例を実行する前にツール バーの [実際の実行プランを含める] をクリックします。

USE tempdb;
GO
DROP TABLE t;
GO
CREATE TABLE t(i int UNIQUE, j int, vc varchar(100));
CREATE INDEX t_vc ON t(vc);
GO
DECLARE @p1 int, @p2 int, @p3 int, @p4 int, @p5 int;
SELECT * FROM t WHERE i IN (@p1, @p2, @p3, @p4, @p5);
GO
DECLARE @p1 int, @p2 int, @p3 int, @p4 int, @p5 int;
SELECT * FROM t WITH (FORCESEEK) WHERE i IN (@p1, @p2, @p3, @p4, @p5);
GO
SELECT * FROM t WHERE vc LIKE 'Test%';
GO
SELECT * FROM t WITH (FORCESEEK) WHERE vc LIKE 'Test%';
GO
DECLARE @vc varchar(100);
SELECT * FROM t WHERE vc LIKE @vc;
GO
DECLARE @vc varchar(100);
SELECT * FROM t WITH (FORCESEEK) where vc like @vc;
GO

USE tempdb;
GO
DROP TABLE t;
GO
CREATE TABLE t(i int UNIQUE, j int, vc varchar(100));
CREATE INDEX t_vc ON t(vc);
GO
DECLARE @p1 int, @p2 int, @p3 int, @p4 int, @p5 int;
SELECT * FROM t WHERE i IN (@p1, @p2, @p3, @p4, @p5);
GO
DECLARE @p1 int, @p2 int, @p3 int, @p4 int, @p5 int;
SELECT * FROM t WITH (FORCESEEK) WHERE i IN (@p1, @p2, @p3, @p4, @p5);
GO
SELECT * FROM t WHERE vc LIKE 'Test%';
GO
SELECT * FROM t WITH (FORCESEEK) WHERE vc LIKE 'Test%';
GO
DECLARE @vc varchar(100);
SELECT * FROM t WHERE vc LIKE @vc;
GO
DECLARE @vc varchar(100);
SELECT * FROM t WITH (FORCESEEK) where vc like @vc;
GO

ビューでの FORCESEEK の使用

FORCESEEK を指定する際には、インデックス ヒントを使用してもしなくてもかまいません。ビューまたはインデックス付きビューに FORCESEEK テーブル ヒントを適用すると、展開した形式のビューに含まれるすべてのテーブルに FORCESEEK ヒントが再帰的に適用されます。インデックス ヒントが指定されている場合は無視されます。基になるテーブルにそれぞれ少なくとも 1 つのインデックスがないと、プランが見つからず、エラー 8622 が返されます。

インデックス付きビューへの参照で FORCESEEK ヒントと NOEXPAND ヒントを一緒に使用すると、インデックス付きビューが展開されずに使用されます。この場合、インデックス付きビューはテーブルと同じように扱われ、直接インデックス付きビューに FORCESEEK ヒントが適用されます。

FORCESEEK ヒントをテーブル参照に適用した場合、そのテーブル参照はインデックス付きビューのマッチングに参加できません。ただし、FORCESEEK ヒントの影響を受けないクエリのその他の部分は、インデックス付きビューのマッチングに参加できます。これは、INDEX ヒントを使用した場合のインデックス付きビューのマッチングの動作に似ています。

推奨設定の注意事項

推奨するベスト プラクティスを次に示します。

  • FORCESEEK テーブル ヒントを使用する前に、データベースの統計が現在のもので正確であることを確認します。

    最新の統計情報により、オプティマイザでは異なるクエリ プランのコストを正確に評価でき、高品質のプランの選択が可能になります。したがって、すべてのユーザー データベースの AUTO_CREATE_STATISTICS および AUTO_UPDATE_STATISTICS を ON (既定値) に設定することをお勧めします。または、UPDATE STATISTICS ステートメントを使用して、テーブルまたはビューの統計を手動で更新することもできます。

  • 基数やコストの不正確な推定の原因になるアイテムがないかどうかクエリを評価し、可能であればそれらのアイテムを取り除きます。たとえば、ローカル変数をパラメータやリテラルに置き換えたり、複数ステートメントのテーブル値関数やテーブル変数のクエリでの使用を制限します。注意が必要なその他のアイテムの詳細については、「Microsoft SQL Server 2005 クエリ オプティマイザが使用する統計情報」を参照してください。

  • 必要がなければ、INDEX ヒントと FORCESEEK を組み合わせて使用しないでください。つまり、FORCESEEK だけで十分なプランが生成される場合、INDEX ヒントも使用するとオプティマイザの選択肢を過度に制限する可能性があります。さらに、テーブルの物理スキーマを変更してヒントに指定されたインデックスを削除すると、INDEX ヒントが原因でクエリがエラーになります。これに対して、FORCESEEK ヒントを適用したテーブルでは、使用可能なインデックスが少なくとも 1 つ存在する限り、インデックス構造を変更したとしてもクエリはコンパイルされます。

  • INDEX ヒント INDEX (0) を FORCESEEK ヒントと組み合わせて使用しないでください。INDEX (0) はベース テーブルを強制的にスキャンします。FORCESEEK と一緒に使用すると、プランが見つからず、エラー 8622 が返されます。

  • USE PLAN クエリ ヒントを FORCESEEK ヒントと組み合わせて使用しないでください。使用すると、FORCESEEK ヒントは無視されます。