プラン ガイドでの INDEX および FORCESEEK のクエリ ヒントの使用
INDEX および FORCESEEK のテーブル ヒントをクエリ ヒントとして指定できます。クエリ ヒントとして指定すると、これらのヒントはインライン テーブルまたはビュー ヒントと同じように動作します。
INDEX ヒントは、指定したインデックスのみを使用して、クエリで参照されているテーブルやビューのデータにアクセスするよう、クエリ オプティマイザに指示します。FORCESEEK ヒントは、インデックスのシーク操作のみを使用して参照先テーブルやビューのデータにアクセスするよう、オプティマイザに指示します。これらのヒントをプラン ガイドの OPTION 句で使用して、クエリの最適化を促すことができます。クエリをプラン ガイドと照合する際、コンパイルや最適化が行われる前に、そのプラン ガイドで指定されている OPTION 句がクエリに追加されます。プラン ガイドの詳細については、「プラン ガイドについて」を参照してください。
注意 |
---|
クエリ ヒントの使用方法が正しくないプラン ガイドは、コンパイル、実行、またはパフォーマンスに関する問題の原因になることがあります。プラン ガイドは、上級開発者とデータベース管理者のみが使用するようにしてください。 |
クエリ ヒントとして指定した場合、INDEX および FORCESEEK テーブル ヒントは次のオブジェクトに対して有効です。
テーブル
ビュー
インデックス付きビュー
共通テーブル式 (ヒントは、結果セットが共通テーブル式に入力される SELECT ステートメントに指定する必要があります)
動的管理ビュー
名前付きサブクエリ
テーブル ヒントは、テーブル値関数、テーブル変数、または OPENROWSET ステートメントには指定できません。
インデックス付きビューのインデックス ヒントを指定するには、OPTION 句で NOEXPAND ヒントも指定する必要があります。NOEXPAND ヒントを指定しない場合、インデックス ヒントは無視されます。詳細については、「ビューのインデックスの解決」を参照してください。
INDEX ヒントおよび FORCESEEK ヒントをクエリ ヒントとして指定するために使用する構文については、「クエリ ヒント (Transact-SQL)」を参照してください。
ベスト プラクティス
推奨するベスト プラクティスを次に示します。
INDEX ヒントおよび FORCESEEK ヒントをクエリ ヒントとして使用するのは、プラン ガイドのコンテキストか、プラン ガイド ステートメントのテスト時のアドホック クエリのみです。その他のアドホック クエリに対しては、これらのヒントをテーブル ヒントとして指定します。
FORCESEEK ヒントを使用する前に、データベースの統計情報が最新かつ正確であることを確認します。
最新の統計情報により、オプティマイザでは異なるクエリ プランのコストを正確に評価でき、高品質のプランの選択が可能になります。したがって、すべてのユーザー データベースの AUTO_CREATE_STATISTICS および AUTO_UPDATE_STATISTICS を ON (既定値) に設定することをお勧めします。または、UPDATE STATISTICS ステートメントを使用して、テーブルまたはビューの統計を手動で更新することもできます。
必要がなければ、INDEX ヒントと FORCESEEK を組み合わせて使用しないでください。FORCESEEK だけで適切なプランが生成される場合、INDEX ヒントも使用するとオプティマイザの選択肢を過度に制限する可能性があります。さらに、テーブルの物理スキーマを変更し、ヒントで指定されているインデックスを削除すると、INDEX ヒントが原因でクエリがエラーになります。一方、FORCESEEK ヒントが適用されるテーブルに使用可能なインデックスが 1 つ以上存在する場合、インデックス構造を変更した場合でもクエリがコンパイルされます。
INDEX ヒント INDEX (0) を FORCESEEK ヒントと組み合わせて使用しないでください。INDEX (0) はベース テーブルを強制的にスキャンします。FORCESEEK と一緒に使用すると、プランが見つからず、エラー 8622 が返されます。
USE PLAN クエリ ヒントを FORCESEEK ヒントと組み合わせて使用しないでください。使用すると、FORCESEEK ヒントは無視されます。
他のテーブル ヒントと組み合わせた INDEX ヒントおよび FORCESEEK ヒントの使用
INDEX および FORCESEEK ヒントは、既存のテーブル ヒントを持たないクエリに指定できます。また、クエリ内の 1 つまたは複数の既存の INDEX または FORCESEEK ヒントの代わりに使用することもできます。プラン ガイドに対応するクエリで、既に WITH 句を使用して、これらのテーブル ヒントが指定されている場合は、クエリ内のヒントの代わりに、プラン ガイドの @hints パラメータで指定されているヒントが使用されます。たとえば、HumanResources.Employee テーブルのテーブル ヒント WITH INDEX (PK_Employee_EmployeeID) がクエリに含まれているときに、プラン ガイドの @hints パラメータに OPTION (TABLE HINT ( HumanResources.Employee, INDEX( IX_Employee_ManagerID ) ) が指定されている場合、クエリ オプティマイザは IX_Employee_ManagerID インデックスを使用します。
INDEX および FORCESEEK 以外のテーブル ヒントは、クエリで既に WITH 句を使用してテーブル ヒントが指定されてない限り、プラン ガイド内のクエリ ヒントとして使用できません。この場合に、そのクエリのセマンティックを維持するには、OPTION 句で TABLE HINT を使用して、対応するヒントもクエリ ヒントとして指定する必要があります。たとえば、クエリにテーブル ヒント NOLOCK が含まれている場合は、プラン ガイドの @hints パラメータの OPTION 句に、INDEX または FORCESEEK テーブル ヒントに加え、NOLOCK ヒントも含まれている必要があります。後の例 C を参照してください。INDEX または FORCESEEK 以外のテーブル ヒントが OPTION 句で TABLE HINT を使用して指定されている一方で対応するクエリ ヒントがない場合 (またはその逆の場合)、OPTION 句によりクエリのセマンティックが変更されることを示すエラー 8702 が発生し、クエリが失敗します。
他のクエリ ヒントと組み合わせた INDEX ヒントおよび FORCESEEK ヒントの使用
プラン ガイドに対応するクエリで、既に OPTION 句を使用してクエリ ヒントが指定されている場合、クエリではそれらのヒントの代わりに、プラン ガイドの @hints パラメータで指定されているクエリ ヒントが使用されます。ただし、既に OPTION 句が使用されているクエリと照合するプラン ガイドでは、sp_create_plan_guide (Transact-SQL) ステートメントで照合するクエリのテキストを指定するときに、そのクエリの OPTION 句を含める必要があります。クエリの既存のヒントに代わりに、プラン ガイドで指定されているヒントを使用するのではなく、プラン ガイドで指定されているヒントを既存のヒントに追加する場合は、プラン ガイドの OPTION 句で、既存のヒントと追加するヒントの両方を指定する必要があります。
例
A. FORCESEEK を使用する
次の例では、プラン ガイドの @hints パラメータに FORCESEEK ヒントを使用しています。このオプションは、インデックスのシーク操作を使用して HumanResources.Employee テーブルのデータにアクセスするよう、オプティマイザに指示します。これにより、オプティマイザは、テーブル ヒントに指定されたインデックス以外のインデックスを使用できるようになります。
USE AdventureWorks;
GO
EXEC sp_create_plan_guide
@name = N'Guide3',
@stmt = N'SELECT c.LastName, c.FirstName, HumanResources.Employee.Title
FROM HumanResources.Employee
JOIN Person.Contact AS c ON HumanResources.Employee.ContactID = c.ContactID
WHERE HumanResources.Employee.ManagerID = 3
ORDER BY c.LastName, c.FirstName;',
@type = N'SQL',
@module_or_batch = NULL,
@params = NULL,
@hints = N'OPTION (TABLE HINT( HumanResources.Employee, FORCESEEK))';
GO
B. 複数のテーブル ヒントを使用する
次の例では、INDEX ヒントと FORCESEEK ヒントをそれぞれ別のテーブルに適用します。
USE AdventureWorks;
GO
EXEC sp_create_plan_guide
@name = N'Guide4',
@stmt = N'SELECT e.ManagerID, c.LastName, c.FirstName, e.Title
FROM HumanResources.Employee AS e
JOIN Person.Contact AS c ON e.ContactID = c.ContactID
WHERE e.ManagerID = 3;',
@type = N'SQL',
@module_or_batch = NULL,
@params = NULL,
@hints = N'OPTION (TABLE HINT ( e, INDEX( IX_Employee_ManagerID ) )
, TABLE HINT ( c, FORCESEEK) )';
GO
C. セマンティックに作用するヒントを指定する
次の例のクエリには、2 つのテーブル ヒントが含まれています。1 つは、セマンティックに作用する NOLOCK で、もう 1 つはセマンティックに作用しない INDEX です。クエリのセマンティックを保持するために、プラン ガイドの OPTIONS 句に NOLOCK ヒントが指定されています。NOLOCK ヒントに加えて、INDEX ヒントと FORCESEEK ヒントが指定されています。ステートメントをコンパイルおよび最適化する際、クエリのセマンティックに作用しない INDEX ヒントの代わりにこれらのヒントが使用されます。
USE AdventureWorks;
GO
EXEC sp_create_plan_guide
@name = N'Guide6',
@stmt = N'SELECT c.LastName, c.FirstName, e.Title
FROM HumanResources.Employee AS e
WITH (NOLOCK, INDEX (PK_Employee_EmployeeID))
JOIN Person.Contact AS c ON e.ContactID = c.ContactID
WHERE e.ManagerID = 3;',
@type = N'SQL',
@module_or_batch = NULL,
@params = NULL,
@hints = N'OPTION (TABLE HINT ( e, INDEX( IX_Employee_ManagerID) , NOLOCK, FORCESEEK ))';
GO
次の例では、クエリのセマンティックを保持し、テーブル ヒントに指定されている以外のインデックスをオプティマイザが選択できるようにする別の方法を示します。これを実現するには、OPTIONS 句に (セマンティックに作用する) NOLOCK ヒントを指定する一方で、INDEX ヒントを持たないテーブル参照だけの TABLE HINT キーワードを指定します。
USE AdventureWorks;
GO
EXEC sp_create_plan_guide
@name = N'Guide7',
@stmt = N'SELECT c.LastName, c.FirstName, e.Title
FROM HumanResources.Employee AS e
WITH (NOLOCK, INDEX (PK_Employee_EmployeeID))
JOIN Person.Contact AS c ON e.ContactID = c.ContactID
WHERE e.ManagerID = 2;',
@type = N'SQL',
@module_or_batch = NULL,
@params = NULL,
@hints = N'OPTION (TABLE HINT ( e, NOLOCK))';
GO
D. TABLE HINT を使用して既存のテーブル ヒントをオーバーライドする
次の例では、INDEX ヒントを指定しないで TABLE HINT を使用し、クエリの FROM 句に指定されている INDEX テーブル ヒントの動作をオーバーライドする方法を示します。この方法により、オプティマイザは、テーブル ヒントに指定されたインデックス以外のインデックスを選択できるようになります。
USE AdventureWorks;
GO
EXEC sp_create_plan_guide
@name = N'Guide5',
@stmt = N'SELECT e.ManagerID, c.LastName, c.FirstName, e.Title
FROM HumanResources.Employee AS e WITH (INDEX (IX_Employee_ManagerID))
JOIN Person.Contact AS c ON e.ContactID = c.ContactID
WHERE e.ManagerID = 3;',
@type = N'SQL',
@module_or_batch = NULL,
@params = NULL,
@hints = N'OPTION (TABLE HINT(e))';
GO