단일 행을 피벗 해제 할 때 도움이되지 않는 병렬 분기를 제거하려면 어떻게해야합니까?


9

소수의 스칼라 집계를 해제하는 다음 쿼리를 고려하십시오.

SELECT A, B
FROM (
    SELECT 
      MAX(CASE WHEN ID = 1 THEN 1 ELSE 0 END) VAL1
    , MAX(CASE WHEN ID = 2 THEN 1 ELSE 0 END) VAL2
    , MAX(CASE WHEN ID = 3 THEN 1 ELSE 0 END) VAL3
    , MAX(CASE WHEN ID = 4 THEN 1 ELSE 0 END) VAL4
    , MAX(CASE WHEN ID = 5 THEN 1 ELSE 0 END) VAL5
    , MAX(CASE WHEN ID = 6 THEN 1 ELSE 0 END) VAL6
    , MAX(CASE WHEN ID = 7 THEN 1 ELSE 0 END) VAL7
    , MAX(CASE WHEN ID = 16 THEN 1 ELSE 0 END) VAL16
    FROM dbo.PARALLEL_ZONE_REPRO
) q
UNPIVOT(B FOR A IN (
    VAL1
    ,VAL2
    ,VAL3
    ,VAL4
    ,VAL5
    ,VAL6
    ,VAL7
    ,VAL16
)) U
OPTION (MAXDOP 4);

SQL Server 2017에서는 두 개의 병렬 분기가있는 계획이 있습니다. 왼쪽 평행 가지가 제 위치에 있지 않습니다. 옵티마이 저는 글로벌 스칼라 집계에서 단일 행 출력 만 보장하지만 상위 연산자는 라운드 로빈 파티셔닝이있는 Distribute Streams입니다.

라운드 로빈

쿼리를 실행하면 모든 행이 예상대로 단일 스레드로 이동합니다. 이 쿼리에는 성능 문제가 없지만 MAXDOP가 4로 설정된 8 개의 병렬 스레드를 예약합니다. 다시 말하지만 이것이 잘못된 것 같습니다. 두 병렬 분기가 동시에 실행되는 것은 불가능합니다. 스케줄러 당 작업자 스레드 수를 확인하도록 스케줄링 알고리즘을 변경하는 TF 2467을 활성화했기 때문에 불필요한 작업자 스레드 예약을 피하고 싶습니다.

테이블 스캔과 로컬 집계를 포함하는 하나의 병렬 분기를 갖도록 쿼리를 다시 작성할 수 있습니까? 예를 들어 중첩 루프를 직렬 영역에서 실행하려는 것을 제외하고는 아래의 일반적인 모양이 좋습니다.

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

Application Reasons ™의 경우이 쿼리를 여러 부분으로 나누지 않는 것이 좋습니다. 원하는 경우 여기 에서 실제 쿼리 계획을 볼 수 있습니다 . 집에서 함께 놀고 싶다면 쿼리에 사용되는 테이블을 만드는 T-SQL이 있습니다.

DROP TABLE IF EXISTS dbo.PARALLEL_ZONE_REPRO;

CREATE TABLE dbo.PARALLEL_ZONE_REPRO (
    ID BIGINT,
    FILLER VARCHAR(100)
);

INSERT INTO dbo.PARALLEL_ZONE_REPRO WITH (TABLOCK)
SELECT
  ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) % 15
, REPLICATE('Z', 100)
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;

답변:


8

다음 사항이 모두 충족되면 직렬 루프 조인으로 원하는 평면 모양을 얻을 수 있습니다.

  • APPLY또는 CROSS JOIN대신에 사용된다UNPIVOT
  • APPLY어떤 외부 참조를 포함하지 않는다
  • 의 행 소스는 APPLY테이블이 아닌 테이블 값 생성자입니다.

예를 들어 다음과 같은 방법이 있습니다.

SELECT A, B
FROM 
(
    SELECT A
    , MAX(
        CASE
            WHEN A = 'VAL1' THEN VAL1 
            WHEN A = 'VAL2' THEN VAL2
            WHEN A = 'VAL3' THEN VAL3
            WHEN A = 'VAL4' THEN VAL4
            WHEN A = 'VAL5' THEN VAL5
            WHEN A = 'VAL6' THEN VAL6
            WHEN A = 'VAL7' THEN VAL7
            WHEN A = 'VAL16' THEN VAL16
            ELSE NULL
        END
    ) B
    FROM (
         SELECT 
           MAX(CASE WHEN ID = 1 THEN 1 ELSE 0 END) VAL1
         , MAX(CASE WHEN ID = 2 THEN 1 ELSE 0 END) VAL2
         , MAX(CASE WHEN ID = 3 THEN 1 ELSE 0 END) VAL3
         , MAX(CASE WHEN ID = 4 THEN 1 ELSE 0 END) VAL4
         , MAX(CASE WHEN ID = 5 THEN 1 ELSE 0 END) VAL5
         , MAX(CASE WHEN ID = 6 THEN 1 ELSE 0 END) VAL6
         , MAX(CASE WHEN ID = 7 THEN 1 ELSE 0 END) VAL7
         , MAX(CASE WHEN ID = 16 THEN 1 ELSE 0 END) VAL16
         FROM dbo.PARALLEL_ZONE_REPRO
    ) q
    CROSS APPLY (
        VALUES ('VAL1'), ('VAL2'), ('VAL3'), ('VAL4'),
        ('VAL5'), ('VAL6'), ('VAL7'), ('VAL16') 
    ) ca (A)
    GROUP BY A
) q
WHERE q.B IS NOT NULL
OPTION (MAXDOP 4);

하나의 병렬 분기로 주장한대로 원하는 계획 계획 형태를 얻습니다.

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

작동하지 않는 다른 많은 것들을 시도했습니다. 이 대답은 왜 작동하는지 모르고 향후 버전의 SQL Server에서는 작동하지 않을 수 있다는 점에서 불만족 스럽지만 내 문제를 해결했습니다.


8

두 병렬 분기가 동시에 실행되는 것은 불가능합니다.

계획 의 왼쪽 가장자리에서 실행이 시작됩니다 . 테이블 스캔 분기가 실행될 때 중첩 루프 분기가 실행 중입니다 (열기, 데이터 대기 중). 이다 피할 수없는 . 두 지점이 동시에 활성화되므로 SQL Server는 이 계획에 대해 2 * DOP 작업자를 예약 합니다.

강력한 솔루션을 위해 피벗을 테이블 반환 함수에 배치 할 수 있습니다.

CREATE OR ALTER FUNCTION dbo.PivotPZR()
RETURNS @R table 
(
    VAL1 bigint NOT NULL, VAL2 bigint NOT NULL,
    VAL3 bigint NOT NULL, VAL4 bigint NOT NULL,
    VAL5 bigint NOT NULL, VAL6 bigint NOT NULL,
    VAL7 bigint NOT NULL, VAL16 bigint NOT NULL
)
WITH SCHEMABINDING AS
BEGIN
    DECLARE 
        @Val1 bigint, @Val2 bigint, @Val3 bigint, @Val4 bigint,
        @Val5 bigint, @Val6 bigint, @Val7 bigint, @Val16 bigint;

    -- Can use parallelism
    SELECT
        @Val1 = MAX(CASE WHEN PZR.ID = 1 THEN 1 ELSE 0 END),
        @Val2 = MAX(CASE WHEN PZR.ID = 2 THEN 1 ELSE 0 END),
        @Val3 = MAX(CASE WHEN PZR.ID = 3 THEN 1 ELSE 0 END),
        @Val4 = MAX(CASE WHEN PZR.ID = 4 THEN 1 ELSE 0 END),
        @Val5 = MAX(CASE WHEN PZR.ID = 5 THEN 1 ELSE 0 END),
        @Val6 = MAX(CASE WHEN PZR.ID = 6 THEN 1 ELSE 0 END),
        @Val7 = MAX(CASE WHEN PZR.ID = 7 THEN 1 ELSE 0 END),
        @Val16 = MAX(CASE WHEN PZR.ID = 16 THEN 1 ELSE 0 END)
    FROM dbo.PARALLEL_ZONE_REPRO AS PZR;

    -- Single result row
    INSERT @R
        (VAL1, VAL2, VAL3, VAL4, VAL5, VAL6, VAL7, VAL16)
    VALUES
        (@Val1, @Val2, @Val3, @Val4, @Val5, @Val6, @Val7, @Val16);

    RETURN;
END;

그런 다음 쿼리를 다음과 같이 다시 작성하십시오.

SELECT
    U.A,
    U.B
FROM dbo.PivotPZR() AS PP
UNPIVOT
(
    B FOR A IN (VAL1, VAL2 ,VAL3 ,VAL4, VAL5 ,VAL6 ,VAL7 ,VAL16)
) AS U;

이 함수는 원하는대로 단일 분기와 병렬 처리를 사용합니다.

기능 계획

최상위 실행 계획은 다음과 같습니다.

최상위 쿼리

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