UNPIVOT (루프 조인)으로 배치 모드를 사용하는 방법은 무엇입니까?


12

다음과 같은 형식의 쿼리가 있습니다.

SELECT ...
FROM ColumnstoreTable cs
CROSS APPLY (
    SELECT *
    FROM (VALUES
        ('A', cs.DataA)
        , ('B', cs.DataB)
        , ('C', cs.DataC)
    ) x(Col0, Col1)
) someValues

이것은 Columnstore 지원 하위 쿼리 ( ColumnstoreTable) 에서 모든 행을 가져와 해당 행을 곱합니다. 이것은 본질적으로 UNPIVOT입니다. 실제 쿼리는 이보다 큽니다. 쿼리의이 부분은 다른 처리로 전달됩니다.

여기서 문제 CROSS APPLY는 이것이 합리적인 선택 인 루프 조인으로 구현 된다는 것 입니다. 불행하게도 루프 조인은 배치 모드를 지원하지 않습니다.

쿼리 의이 부분은 성능이 매우 중요하므로 일괄 처리 모드로 실행하면 성능에 매우 도움이 될 것으로 생각됩니다.

일괄 처리 모드에서 전환되지 않도록이 쿼리를 어떻게 다시 작성할 수 있습니까?

대신 임시 테이블을 사용해 VALUES보았지만 해시 조인에 대한 동등 조인 조건이 없다는 사실은 변경되지 않았습니다.

답변:


12

한 가지 방법은 값에 #temp 테이블을 사용하고 해시 조인을 허용하기 위해 더미 equijoin 열을 도입하는 것입니다. 예를 들면 다음과 같습니다.

-- Create a #temp table with a dummy column to match the hash join
-- and the actual column you want
CREATE TABLE #values (dummy INT NOT NULL, Col0 CHAR(1) NOT NULL)
INSERT INTO #values (dummy, Col0)
VALUES (0, 'A'),
        (0, 'B'),
        (0, 'C')
GO

-- A similar query, but with a dummy equijoin condition to allow for a hash join
SELECT v.Col0, 
    CASE v.Col0 
        WHEN 'A' THEN cs.DataA 
        WHEN 'B' THEN cs.DataB 
        WHEN 'C' THEN cs.DataC
        END AS Col1
FROM ColumnstoreTable cs
JOIN #values v
    -- Join your dummy column to any numeric column on the columnstore,
    -- multiplying that column by 0 to ensure a match to all #values
    ON v.dummy = cs.DataA * 0

성능 및 쿼리 계획

이 방법은 다음과 같은 쿼리 계획을 생성하며 해시 일치는 배치 모드에서 수행됩니다.

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

나는 대체 할 경우 SELECT로 문을 SUMCASE콘솔에 모든 행을 스트리밍하고 내가 주위에 거짓말을 한 진정한 100MM 행 columnstore 테이블에 쿼리를 실행하는 것을 방지하기 위해 문, 나는 상당히 좋은 성능이 필요한 300MM를 생성하기 위해 참조 행 :

CPU time = 33803 ms, elapsed time = 4363 ms.

그리고 실제 계획은 해시 조인의 좋은 병렬화를 보여줍니다.

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

모든 행의 값이 동일한 경우 해시 조인 병렬화에 대한 참고 사항

이 쿼리의 성능은 전체 해시 테이블에 액세스 할 수있는 조인의 프로브쪽에있는 각 스레드에 크게 의존합니다 (해시 분할 버전과는 달리, 하나의 고유 값만있는 경우 모든 행을 단일 스레드에 매핑 함) 위한 dummy열).

운 좋게도,이 경우 ( Parallelism프로브 측에 연산자가 없기 때문에) 배치 모드는 스레드간에 공유되는 단일 해시 테이블을 빌드하므로 확실하게 사실이어야합니다. 따라서 각 스레드는에서 행을 가져 와서 Columnstore Index Scan해당 공유 해시 테이블과 일치시킬 수 있습니다. SQL Server 2012에서는 유출로 인해 운영자가 행 모드에서 다시 시작하여 배치 모드의 이점을 잃고 Repartition Streams조인의 프로브 측에 운영자가 스레드 스큐를 일으킬 수 있기 때문에 예측하기가 훨씬 어려웠습니다. . 유출이 배치 모드로 유지되도록하는 것은 SQL Server 2014에서 크게 개선되었습니다.

내 지식으로는 행 모드에는이 공유 해시 테이블 기능이 없습니다. 그러나 경우에 따라 일반적으로 빌드 쪽에서 행 수가 100 개 미만인 SQL Server는 각 스레드에 대해 별도의 해시 테이블 복사본을 만듭니다 ( Distribute Streams해시 조인으로 선행하여 식별 가능 ). 이것은 매우 강력 할 수 있지만 카디널리티 예상 값에 따라 다르며 SQL Server는 각 스레드에 대한 해시 테이블의 전체 복사본을 작성하는 비용과 비교하여 이점을 평가하려고하기 때문에 배치 모드보다 훨씬 덜 안정적입니다.

UNION ALL : 더 간단한 대안

Paul White는 또 다른 잠재적으로 더 간단한 옵션은 UNION ALL각 값의 행을 결합하는 데 사용 하는 것이라고 지적했습니다 . 이 SQL을 동적으로 쉽게 구축 할 수 있다고 가정하면 가장 좋은 방법 일 것입니다. 예를 들면 다음과 같습니다.

SELECT 'A' AS Col0, c.DataA AS Col1
FROM ColumnstoreTable c
UNION ALL
SELECT 'B' AS Col0, c.DataB AS Col1
FROM ColumnstoreTable c
UNION ALL
SELECT 'C' AS Col0, c.DataC AS Col1
FROM ColumnstoreTable c

또한 배치 모드를 사용할 수 있고 원래 답변보다 더 나은 성능을 제공하는 계획을 산출합니다. (두 경우 모두 성능이 빠르기 때문에 데이터를 테이블에 선택하거나 테이블에 쓰면 신속하게 병목 현상이 발생합니다.)이 UNION ALL방법은 0을 곱하는 것과 같은 게임은 피합니다. 때로는 단순하게 생각하는 것이 가장 좋습니다!

CPU time = 8673 ms, elapsed time = 4270 ms.

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


UNION ALL 트릭을 아는 것이 좋지만 소스 쿼리가 단순한 테이블이 아니라 비용이 많이 드는 쿼리 인 경우에는 실패합니다.
boot4life
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.