일정한 스캔 스풀링


14

수십 개의 행이있는 테이블이 있습니다. 단순화 된 설정은 다음과 같습니다

CREATE TABLE #data ([Id] int, [Status] int);

INSERT INTO #data
VALUES (100, 1), (101, 2), (102, 3), (103, 2);

그리고이 테이블을 일련의 테이블 값으로 구성된 행 (변수 및 상수로 구성)에 조인하는 쿼리가 있습니다.

DECLARE @id1 int = 101, @id2 int = 105;

SELECT
    COALESCE(p.[Code], 'X') AS [Code],
    COALESCE(d.[Status], 0) AS [Status]
FROM (VALUES
        (@id1, 'A'),
        (@id2, 'B')
    ) p([Id], [Code])
    FULL JOIN #data d ON d.[Id] = p.[Id];

쿼리 실행 계획은 옵티마이 저의 결정이 FULL LOOP JOIN전략 을 사용하는 것임을 보여줍니다. 두 입력 모두 행이 거의 없기 때문에 적절한 것으로 보입니다. 그러나 내가 알았고 동의 할 수없는 한 가지는 TVC 행이 스풀링되고 있다는 것입니다 (빨간색 상자의 실행 계획 영역 참조).

일정한 스캔 스풀링

옵티마이 저가 스풀을 여기에 도입하는 이유는 무엇입니까? 스풀 외에는 복잡한 것이 없습니다. 필요하지 않은 것 같습니다. 이 경우 그것을 제거하는 방법, 가능한 방법은 무엇입니까?


위의 계획은

Microsoft SQL Server 2014 (SP2-CU11) (KB4077063)-12.0.5579.0 (X64)


feedback.azure.com의 관련 제안
i-one

답변:


19

옵티마이 저가 스풀을 여기에 도입하는 이유는 무엇입니까? 스풀 외에는 복잡한 것이 없습니다.

스풀 이외의 것은 간단한 테이블 참조가 아니며 왼쪽 조인 / 반 세미 조인 대안 이 생성 될 때 단순히 복제 될 수 있습니다 .

그것은 수 보이는 작은 테이블 (상수 스캔)처럼하지만, 그것은이다 * 최적화에 UNION ALL에서 별도의 행 VALUES절.

추가적인 복잡성은 옵티마이 저가 소스 행을 스풀링하고 재생하도록 선택하기에 충분하며 나중에 스풀을 단순한 "테이블 가져 오기"로 대체하지 않습니다. 예를 들어 전체 조인의 초기 변환은 다음과 같습니다.

초기 계획

일반 변환에서 도입 된 추가 스풀을 확인하십시오. 간단한 테이블 가져 오기 위의 스풀은 나중에 규칙에 의해 정리됩니다 SpoolGetToGet.

옵티 마이저에 해당 SpoolConstGetToConstGet규칙 이있는 경우 원칙적으로 원하는대로 작동 할 수 있습니다.

이 경우 그것을 제거하는 방법, 가능한 방법은 무엇입니까?

실제 테이블 (임시 또는 변수)을 사용하거나 다음과 같이 전체 조인에서 수동으로 변환을 작성하십시오.

WITH 
    p([Id], [Code]) AS
    (
        SELECT @id1, 'A'
        UNION ALL
        SELECT @id2, 'B'
    ),
    FullJoin AS
    (
        SELECT
            p.Code,
            d.[Status]
        FROM p
        LEFT JOIN #data d 
            ON d.[Id] = p.[Id]
        UNION ALL
        SELECT
            NULL,
            D.[Status]
        FROM #data AS D
        WHERE NOT EXISTS
        (
            SELECT *
            FROM p
            WHERE p.Id = D.Id
        )
    )
SELECT
    COALESCE(FullJoin.Code, 'X') AS Code,
    COALESCE(FullJoin.Status, 0) AS [Status]
FROM FullJoin;

수동 재 작성 계획 :

수동 재 작성 계획

원래의 0.0203412 단위와 비교하여 추정 비용은 0.0067201 단위입니다.


* 이것은 변환 된 트리 (TF 8605) LogOp_UnionAll에서 볼 수 있습니다 . 에서 입력 트리 (TF 8606) 그것은이다 . 변환 트리 쇼 구문 분석, 표준화, algebrization, 바인딩, 그리고 다른 준비 작업 후 최적화 표현 요소의 나무. 입력 트리 부정 정규형 (NNF 변환), 붕괴 런타임 상수 및 다른 약간 잡동사니에 변환 후의 요소를 나타낸다. NNF 변환에는 무엇보다도 논리 합집합과 공통 테이블 가져 오기를 축소하는 논리가 포함됩니다.LogOp_ConstTableGet


3

테이블 스풀은 단순히 VALUES절 에있는 두 개의 튜플 세트에서 테이블을 작성하는 것입니다 .

다음과 같이 해당 값을 임시 테이블에 먼저 삽입하여 스풀을 제거 할 수 있습니다.

DROP TABLE IF EXISTS #data;
CREATE TABLE #data ([Id] int, [Status] int);

INSERT INTO #data
VALUES (100, 1), (101, 2), (102, 3), (103, 2);

DROP TABLE IF EXISTS #p;
CREATE TABLE #p
(
    Id int NOT NULL
    , Code char(1) NOT NULL
);

DECLARE @id1 int = 101, @id2 int = 105;

INSERT INTO #p (Id, Code)
VALUES
        (@id1, 'A'),
        (@id2, 'B');


SELECT
    COALESCE(p.[Code], 'X') AS [Code],
    COALESCE(d.[Status], 0) AS [Status]
FROM #p p
    FULL JOIN #data d ON d.[Id] = p.[Id];

쿼리의 실행 계획을 보면 출력 목록에 Union접두사 를 사용하는 두 개의 열이 포함되어 있습니다 . 이것은 스풀이 통합 소스에서 테이블을 작성하고 있음을 암시합니다.

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

FULL OUTER JOIN의 값에 액세스하는 SQL Server가 필요 p조인의 각 "측면"을 위해 한 번, 두 번. 스풀을 작성하면 결과 내부 루프 결합이 스풀 된 데이터에 액세스 할 수 있습니다.

당신은 대체하면 흥미롭게도, FULL OUTER JOINA를 LEFT JOIN하고 RIGHT JOIN, 그리고 UNION함께 결과를, SQL Server는 스풀을 사용하지 않습니다.

SELECT
    COALESCE(p.[Code], 'X') AS [Code],
    COALESCE(d.[Status], 0) AS [Status]
FROM (VALUES
        (101, 'A'),
        (105, 'B')
    ) p([Id], [Code])
    LEFT JOIN #data d ON d.[Id] = p.[Id]
UNION
SELECT
    COALESCE(p.[Code], 'X') AS [Code],
    COALESCE(d.[Status], 0) AS [Status]
FROM (VALUES
        (101, 'A'),
        (105, 'B')
    ) p([Id], [Code])
    RIGHT JOIN #data d ON d.[Id] = p.[Id];

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

UNION위 의 쿼리를 사용하는 것은 제안하지 않습니다 . 더 큰 입력 세트의 경우 기존의 것보다 효율적이지 않을 수 FULL OUTER JOIN있습니다.


실제 워크로드에서 스풀은 실제로 비용이 많이 듭니까?
Max Vernon
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.