パラメータスニッフィングの詳細については説明しませんが、要するに、常にではありません。 助けてください(そしてそれは妨げる可能性があります)。
主キーとインデックス付きの日付列(A)を持つテーブル(T)を想像してみてください。テーブルには、1,000行あり、400行はAと同じ値であり(今日は20130122としましょう)、残りの600行は次の600日です。 、したがって、日付ごとに1つのレコードのみ。
このクエリ:
SELECT *
FROM T
WHERE A = '20130122';
次のような別の実行プランが作成されます:
SELECT *
FROM T
WHERE A = '20130123';
統計では、1,000行のうち最初の400行が返されることが示されているため、オプティマイザーは、テーブルスキャンがブックマークルックアップよりも効率的であるのに対し、2番目の行では1行しか生成されないため、ブックマークルックアップが多くなることを認識します。より効率的です。
さて、これを手順にした場合、あなたの質問に戻りましょう:
CREATE PROCEDURE dbo.GetFromT @Param DATE
AS
SELECT *
FROM T
WHERE A = @Param
次に実行します
EXECUTE dbo.GetFromT '20130122'; --400 rows
テーブルスキャンを使用したクエリプランが使用されます。初めて実行する場合は、パラメータとして「20130123」を使用すると、ブックマークルックアッププランが保存されます。手順が再コンパイルされるまで、計画は同じままです。このようなことをする:
CREATE PROCEDURE dbo.GetFromT @Param VARCHAR(5)
AS
DECLARE @Param2 VARCHAR(5) = @Param;
SELECT *
FROM T
WHERE A = @Param2
次に、これが実行されます:
EXECUTE dbo.GetFromT '20130122';
プロシージャは一度にコンパイルされますが、適切にフローされないため、最初のコンパイルで作成されたクエリプランは、@ Param2が@paramと同じになることを認識していないため、オプティマイザ(期待)は、300が返される(30%)と想定します。そのため、ブックマークルックアップよりもテーブルスキャンの方が効率的であると見なされます。 '20130123'をパラメーターとして使用して同じプロシージャを実行した場合、統計を不明な値に使用できないため、(最初に呼び出されたパラメーターに関係なく)同じプランが生成されます。したがって、このプロシージャを「20130122」に対して実行する方が効率的ですが、他のすべての値に対しては、ローカルパラメータなしの場合よりも効率が低くなります(ローカルパラメータなしのプロシージャが最初に「20130122」以外で呼び出されたと仮定)
実行プランを自分で表示できるようにするためのいくつかのクエリ
スキーマとサンプルデータを作成する
CREATE TABLE T (ID INT IDENTITY(1, 1) PRIMARY KEY, A DATE NOT NULL, B INT,C INT, D INT, E INT);
CREATE NONCLUSTERED INDEX IX_T ON T (A);
INSERT T (A, B, C, D, E)
SELECT TOP 400 CAST('20130122' AS DATE), number, 2, 3, 4
FROM Master..spt_values
WHERE type = 'P'
UNION ALL
SELECT TOP 600 DATEADD(DAY, number, CAST('20130122' AS DATE)), number, 2, 3, 4
FROM Master..spt_values
WHERE Type = 'P';
GO
CREATE PROCEDURE dbo.GetFromT @Param DATE
AS
SELECT *
FROM T
WHERE A = @Param
GO
CREATE PROCEDURE dbo.GetFromT2 @Param DATE
AS
DECLARE @Param2 DATE = @Param;
SELECT *
FROM T
WHERE A = @Param2
GO
実行手順(実際の実行計画を示す):
EXECUTE GetFromT '20130122';
EXECUTE GetFromT '20130123';
EXECUTE GetFromT2 '20130122';
EXECUTE GetFromT2 '20130123';
GO
EXECUTE SP_RECOMPILE GetFromT;
EXECUTE SP_RECOMPILE GetFromT2;
GO
EXECUTE GetFromT '20130123';
EXECUTE GetFromT '20130122';
EXECUTE GetFromT2 '20130123';
EXECUTE GetFromT2 '20130122';
初めてGetFromT
コンパイルされ、テーブルスキャンを使用し、パラメータ「20130122」、GetFromT2
を指定して実行するとこれが保持されます。 また、テーブルスキャンを使用し、「20130122」の計画を保持します。
プロシージャが再コンパイル用に設定され、再度実行された後(別の順序で注意してください)、GetFromT
ブックマークループアップを使用し、テーブルスキャンがより適切な計画であると以前に考えていたにもかかわらず、「20130122」の計画を保持します。 GetFromT2
注文の影響を受けず、再コンパイル前と同じ計画です。
したがって、要約すると、データの分布、インデックス、再コンパイルの頻度、およびプロシージャがローカル変数を使用することでメリットが得られるかどうかについての少しの運に依存します。確かに常にではありません ヘルプ。
うまくいけば、ローカルパラメータ、実行プラン、およびストアドプロシージャのコンパイルを使用した場合の影響に光を当てることができます。私が完全に失敗した場合、または重要なポイントを見逃した場合は、ここでさらに詳細な説明を見つけることができます:
http://www.sommarskog.se/query-plan-mysteries.html