SQL Server-- 저장 프로 시저 및 계획 캐시의 논리


15

SQL Server 2012 및 2016 표준 :

if-else매개 변수 값에 따라 두 가지 코드 분기 중 하나를 실행하기 위해 저장 프로 시저 에 논리를 배치 하면 엔진이 최신 버전을 캐시합니까?

그리고 다음 실행에서 매개 변수의 값이 변경되면 코드의 다른 분기를 실행해야하기 때문에 저장 프로 시저를 다시 컴파일하고 다시 캐시 합니까? 이 쿼리는 컴파일하는 데 비용이 많이 듭니다.

답변:


27

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;

도움이 되었기를 바랍니다!

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.