OPTION (RECOMPILE)이 아니면 인덱스 SEEK를 사용하지 않습니까?


11

(질문이 SO에서 옮겨졌습니다.)

클러스터 된 인덱스가있는 테이블 (더미 데이터)에 2 개의 열이 있습니다.

여기에 이미지 설명을 입력하십시오

이제 두 가지 쿼리를 실행합니다.

declare 
@productid int =1 , 
@priceid  int = 1




SELECT productid,
       t.priceID
FROM   Transactions AS t
WHERE  (productID = @productid OR @productid IS NULL)
       AND (priceid = @priceid OR @priceid IS NULL)  


SELECT productid,
       t.priceID
FROM   Transactions AS t
WHERE  (productID = @productid)
       AND (priceid = @priceid)

두 쿼리의 실제 실행 계획은 다음과 같습니다.

여기에 이미지 설명을 입력하십시오

보다시피, 첫 번째는 SCAN을 사용하고 두 번째는 SEEK를 사용하고 있습니다.

그러나 OPTION (RECOMPILE)첫 번째 쿼리에 추가 하여 실행 계획에서도 SEEK를 사용했습니다.

여기에 이미지 설명을 입력하십시오

DBA 채팅 친구들은 다음과 같이 말했습니다.

쿼리에서 @ productid = 1은 (productID = @ productID 또는 @productID IS NULL)을 (productID = @ productID)로 단순화 할 수 있음을 의미합니다. 전자는 @productID의 값으로 작동하려면 스캔이 필요하고 후자는 검색을 사용할 수 있습니다. 따라서 RECOMPILE을 사용할 때 SQL Server는 실제로 @productID에 어떤 값이 있는지 확인하고 최상의 계획을 세웁니다. @productID에 null이 아닌 값을 사용하면 검색이 가장 좋습니다. @productID의 값을 알 수없는 경우 계획은 @productID의 가능한 모든 값에 맞아야하며 스캔이 필요합니다. 경고 : OPTION (RECOMPILE)은 계획을 실행할 때마다 계획을 다시 컴파일하여 실행마다 몇 밀리 초를 추가합니다. 쿼리가 매우 자주 실행되는 경우에만 문제가됩니다.

또한 :

@productID가 null이면 어떤 값을 찾고 싶습니까? 답 : 추구 할 것이 없습니다. 모든 가치가 있습니다.

나는 이해 OPTION (RECOMPILE)세력 SQL 서버는이 값 매개 변수 실제 무엇을보고, 그것으로 추구 할 수 있는지.

그러나 이제는 사전 컴파일의 이점을 잃습니다.

질문

IMHO-SCAN은 param이 null 인 경우에만 발생합니다.
괜찮습니다-SQL SERVER가 SCAN에 대한 실행 계획을 작성하게하십시오.
그러나 SQL Server 에서이 쿼리를 여러 번 실행하여 값을 여러 번 1,1실행하면 다른 실행 계획을 작성하고 SEEK를 사용하지 않는 이유는 무엇입니까?

AFAIK-SQL은 가장 인기있는 쿼리에 대한 실행 계획을 만듭니다 .

  • SQL Server가 다음에 대한 실행 계획을 저장하지 않는 이유는 무엇입니까?

    @productid int =1 , @priceid int = 1

(나는 그 값으로 여러 번 실행합니다)

  • 향후 호출을 위해 SQL이 해당 실행 계획 (SEEK를 사용함)을 유지하도록 할 수 있습니까?

전체 테이블 스크립트 + 데이터 생성


답변:


10

대화방 토론 에서 주요 요점을 요약하면 다음과 같습니다.


일반적으로 SQL Server 는 각 문마다 단일 계획을 캐시합니다 . 이 계획은 향후 모든 가능한 매개 변수 값에 유효 해야 합니다 .

예를 들어 @productid 가 null 인 경우 해당 계획이 유효하지 않으므로 쿼리에 대한 탐색 계획 을 캐시 할 수 없습니다 .

이후 릴리스에서 SQL Server는 런타임 매개 변수 값에 따라 검색과 탐색 중에서 동적으로 선택하는 단일 계획을 지원할 수 있지만 현재는 그렇지 않습니다.

일반적인 문제 클래스

쿼리는 "모두 캐치"또는 "동적 검색"쿼리라고하는 패턴의 예입니다. 각각 고유 한 장단점이있는 다양한 솔루션이 있습니다. 최신 버전의 SQL Server (2008+)에서 주요 옵션은 다음과 같습니다.

  • IF 블록
  • OPTION (RECOMPILE)
  • 사용하는 동적 SQL sp_executesql

이 주제에 대한 가장 포괄적 인 작업은 아마도 Erland Sommarskog의 글일 것입니다.이 답변은이 답변의 끝에 참고 문헌에 포함되어 있습니다. 관련된 복잡성을 피할 수는 없으므로 각 경우의 장단점을 이해하기 위해 각 옵션을 시도하는 데 약간의 시간을 투자해야합니다.

IF 블록

문제 IF의 특정 사례에 대한 블록 솔루션 을 설명하려면 :

IF @productid IS NOT NULL AND @priceid IS NOT NULL
BEGIN
    SELECT 
        T.productID,
        T.priceID
    FROM dbo.Transactions AS T
    WHERE
        T.productID = @productid
        AND T.priceID = @priceid;
END;
ELSE IF @productid IS NOT NULL
BEGIN
    SELECT 
        T.productID,
        T.priceID
    FROM dbo.Transactions AS T
    WHERE
        T.productID = @productid;
END;
ELSE IF @priceid IS NOT NULL
BEGIN
    SELECT 
        T.productID,
        T.priceID
    FROM dbo.Transactions AS T
    WHERE
        T.priceID = @priceid;
END;
ELSE
BEGIN
    SELECT 
        T.productID,
        T.priceID
    FROM dbo.Transactions AS T;
END;

여기에는 두 매개 변수 (또는 로컬 변수) 각각에 대해 널 (NULL) 또는 널 (null)이 아닌 네 가지 경우에 대한 별도의 명령문이 포함되므로 네 가지 계획이 있습니다.

매개 변수 스니핑에 잠재적 인 문제가 있으며 OPTIMIZE FOR각 쿼리에 대한 힌트 가 필요할 수 있습니다. 이러한 유형의 미묘함을 살펴 보려면 참조 섹션을 참조하십시오.

재 컴파일

질문에서 위에서 언급했듯이 OPTION (RECOMPILE)각 호출에 대해 새로운 계획 (검색 또는 스캔)을 얻는 힌트를 추가 할 수도 있습니다 . 귀하의 경우에 상대적으로 느린 빈도의 전화 (평균 밀리 초 미만의 컴파일 시간으로 10 초마다 한 번)를 고려하면이 옵션이 적합 할 것 같습니다.

SELECT
    T.productID,
    T.priceID
FROM dbo.Transactions AS T
WHERE
    (T.productID = @productid OR @productid IS NULL)
    AND (T.priceID = @priceid OR @priceid IS NULL)
OPTION (RECOMPILE);

또한 위의 옵션의 기능을 창의적으로 결합하여 각 방법의 장점을 최대한 활용하면서 단점을 최소화 할 수도 있습니다. 실제로 이러한 것들을 자세히 이해하는 데 지름길이 없으며 현실적인 테스트를 통해 정보에 입각 한 선택을합니다.

추가 자료

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