답변:
SQL Server 2012 및 2016 표준 : 매개 변수 값에 따라 if-else 논리를 저장 프로 시저에 넣어 두 가지 코드 중 하나를 실행하면 엔진이 최신 버전을 캐시합니까?
아니요, 모든 버전을 캐시 합니다 . 또는 전달 된 모든 경로 와 함께 전달 된 변수로 컴파일 된 하나의 버전을 캐시 합니다.
다음은 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;
저장된 proc을 실행하여 Reputation = 1을 찾으면 오류가 발생합니다.
EXEC dbo.YourMom @Reputation = 1;
메시지 308, 수준 16, 상태 1, 절차 YourMom, 줄 14 [배치 시작 줄 32] 'dbo.Users'테이블에 'ix_yourdad'인덱스 (FROM 절에 지정)가 없습니다.
인덱스 이름을 수정하고 쿼리를 다시 실행하면 캐시 된 계획 은 다음과 같습니다.
내부에서 XML에는 @Reputation
변수에 대한 두 개의 참조가 있습니다 .
<ColumnReference Column="@Reputation" ParameterDataType="int" ParameterCompiledValue="(1)" />
약간 더 간단한 테스트는 저장된 proc에 대한 예상 계획을 얻는 것입니다. 옵티마이 저가 두 경로를 탐색하는 것을 볼 수 있습니다.
그리고 다음 실행에서 매개 변수의 값이 변경되면 다른 코드 분기를 실행해야하기 때문에 저장 프로 시저를 다시 컴파일하고 다시 캐시합니까? 이 쿼리는 컴파일하는 데 비용이 많이 듭니다. 감사합니다.
아니요, 첫 번째 컴파일의 런타임 값을 유지합니다.
우리가 다른 것으로 다시 실행하면 @Reputation
:
EXEC dbo.YourMom @Reputation = 2;
로부터 실제 계획 :
<ColumnReference Column="@Reputation" ParameterDataType="int" ParameterCompiledValue="(1)" ParameterRuntimeValue="(2)" />
여전히 컴파일 된 값은 1이지만 런타임 값은 2입니다.
회사에서 개발 한 것과 같은 무료 도구 인 sp_BlitzCache로 체크 아웃 할 수있는 계획 캐시에서 :
스토어드 프로 시저가 두 번 호출되었으며 각 프로 시저가 한 번 호출되었습니다.
그래서 우리는 무엇을해야합니까? 저장 프로 시저의 두 쿼리에 대한 하나의 캐시 된 계획.
이런 종류의 분기 논리 를 원하면 하위 저장 프로 시저를 호출해야합니다.
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;
도움이 되었기를 바랍니다!