回答:
SQL Server 2012および2016 Standard:ストアドプロシージャにif-elseロジックを配置して、パラメーターの値に応じて2つのコードブランチのいずれかを実行すると、エンジンは最新バージョンをキャッシュしますか?
いいえ、すべてのバージョンをキャッシュします。むしろ、渡された変数でコンパイルされたすべてのパスが探索された1つのバージョンをキャッシュします。
Stack Overflowデータベースを使用した簡単なデモを次に示します。
インデックスを作成します。
CREATE INDEX ix_yourmom ON dbo.Users (Reputation) INCLUDE (Id, DisplayName);
GO
分岐コード内に存在しないインデックスを指すインデックスヒントを持つストアドプロシージャを作成します。
CREATE OR ALTER PROCEDURE dbo.YourMom (@Reputation INT)
AS
BEGIN
IF @Reputation = 1
BEGIN
SELECT u.Id, u.DisplayName, u.Reputation
FROM dbo.Users AS u WITH (INDEX = PK_Users_Id)
WHERE u.Reputation = @Reputation;
END;
IF @Reputation > 1
BEGIN
SELECT u.Id, u.DisplayName, u.Reputation
FROM dbo.Users AS u WITH (INDEX = ix_yourdad)
WHERE u.Reputation = @Reputation;
END;
END;
Reputation = 1を探してそのストアドプロシージャを実行すると、エラーが発生します。
EXEC dbo.YourMom @Reputation = 1;
メッセージ308、レベル16、状態1、プロシージャYourMom、行14 [バッチ開始行32]テーブル 'dbo.Users'(FROM句で指定)のインデックス 'ix_yourdad'は存在しません。
インデックス名を修正してクエリを再実行すると、キャッシュされたプランは次のようになります。
内部では、XMLには@Reputation
変数への2つの参照があります。
<ColumnReference Column="@Reputation" ParameterDataType="int" ParameterCompiledValue="(1)" />
少し簡単なテストは、ストアドプロシージャの推定プランを取得することです。オプティマイザーが両方のパスを探索していることがわかります。
また、次の実行時にパラメータの値が変更された場合、コードの別のブランチを実行する必要があるため、ストアドプロシージャを再コンパイルおよび再キャッシュしますか?(このクエリのコンパイルにはかなりの費用がかかります。)ありがとうございます。
いいえ、最初のコンパイルのランタイム値を保持します。
別のもので再実行する場合@Reputation
:
EXEC dbo.YourMom @Reputation = 2;
実際の計画から:
<ColumnReference Column="@Reputation" ParameterDataType="int" ParameterCompiledValue="(1)" ParameterRuntimeValue="(2)" />
コンパイルされた値は1のままですが、実行時の値は2になりました。
私の会社が開発しているような無料ツールsp_BlitzCacheでチェックアウトできるプランキャッシュで:
ストアドプロシージャが2回呼び出され、その中の各ステートメントが1回呼び出されました。
それで、私たちには何がありますか?ストアドプロシージャの両方のクエリに対して1つのキャッシュされたプラン。
あなたがいる場合したい分岐したロジックのこの種を、あなたはサブストアドプロシージャを呼び出す必要があるだろう。
CREATE OR ALTER PROCEDURE dbo.YourMom (@Reputation INT)
AS
BEGIN
IF @Reputation = 1
BEGIN
EXEC dbo.Reputation1Query;
END;
IF @Reputation > 1
BEGIN
EXEC dbo.ReputationGreaterThan1Query;
END;
END;
または動的SQL:
DECLARE @sql NVARCHAR(MAX) = N''
SET @sql +=
N'
SELECT u.Id, u.DisplayName, u.Reputation
FROM dbo.Users AS u '
IF @Reputation = 1
BEGIN
SET @sql += N' (INDEX = PK_Users_Id)
WHERE u.Reputation = @Reputation;'
END;
IF @Reputation > 1
BEGIN
SET @sql += ' WITH (INDEX = ix_yourmom)
WHERE u.Reputation = @Reputation;'
END;
EXEC sys.sp_executesql @sql;
お役に立てれば!