SQL Server에서 다 대다 조인을 암시하는 방법은 무엇입니까?


9

한 쌍의 열 (둘 다 int) 에 조인되는 3 개의 "큰"테이블이 있습니다.

  • Table1에는 ~ 2 억 개의 행이 있습니다.
  • Table2 ~ 150 만 행
  • Table3에는 ~ 600 만 개의 행이 있습니다.

각 테이블에는 클러스터 인덱스가 Key1, Key2다음, 하나 더 열을. Key1카디널리티가 낮고 매우 비뚤어집니다. 항상 WHERE조항 에서 참조됩니다 . 절 Key2에서 언급되지 않았습니다 WHERE. 각 조인은 다 대다입니다.

문제는 카디널리티 추정에 있습니다. 각 조인의 출력 추정치가 커지는 대신 작아 집니다 . 결과는 실제 결과가 수백만에 달할 때 최종 수백 명이 줄어 듭니다.

CE가 더 나은 견적을 낼 수있는 방법이 있습니까?

SELECT 1
FROM Table1 t1
     JOIN Table2 t2
       ON t1.Key1 = t2.Key1
          AND t1.Key2 = t2.Key2
     JOIN Table3 t3
       ON t1.Key1 = t3.Key1
          AND t1.Key2 = t3.Key2
WHERE t1.Key1 = 1;

내가 시도한 솔루션 :

  • 에 다중 열 통계 작성 Key1,Key2
  • 만들기 에 필터링 된 통계를 Key1(이 꽤 도움이되지만 나는 데이터베이스에 사용자가 만든 통계의 수천 끝.)

마스킹 된 실행 계획 (나쁜 마스킹에 대해 죄송합니다)

내가보고있는 경우 결과에는 9 백만 개의 행이 있습니다. 새로운 CE는 180 개의 행을 추정합니다. 레거시 CE는 6100 개의 행을 추정합니다.

재현 가능한 예는 다음과 같습니다.

DROP TABLE IF EXISTS #Table1, #Table2, #Table3;
CREATE TABLE #Table1 (Key1 INT NOT NULL, Key2 INT NOT NULL, T1Key3 INT NOT NULL, CONSTRAINT pk_t1 PRIMARY KEY CLUSTERED (Key1, Key2, T1Key3));
CREATE TABLE #Table2 (Key1 INT NOT NULL, Key2 INT NOT NULL, T2Key3 INT NOT NULL, CONSTRAINT pk_t2 PRIMARY KEY CLUSTERED (Key1, Key2, T2Key3));
CREATE TABLE #Table3 (Key1 INT NOT NULL, Key2 INT NOT NULL, T3Key3 INT NOT NULL, CONSTRAINT pk_t3 PRIMARY KEY CLUSTERED (Key1, Key2, T3Key3));

-- Table1 
WITH Numbers
     AS (SELECT TOP (1000000) Number = ROW_NUMBER() OVER(ORDER BY t1.number)
         FROM master..spt_values t1
              CROSS JOIN master..spt_values t2),
     DataSize (Key1, NumberOfRows)
     AS (SELECT 1, 2000 UNION
         SELECT 2, 10000 UNION
         SELECT 3, 25000 UNION
         SELECT 4, 50000 UNION
         SELECT 5, 200000)
INSERT INTO #Table1
SELECT Key1
     , Key2 = ROW_NUMBER() OVER (PARTITION BY Key1, T1Key3 ORDER BY Number)
     , T1Key3
FROM DataSize
     CROSS APPLY (SELECT TOP(NumberOfRows) 
                         Number
                       , T1Key3 = Number%(Key1*Key1) + 1 
                  FROM Numbers
                  ORDER BY Number) size;

-- Table2 (same Key1, Key2 values; smaller number of distinct third Key)
WITH Numbers
     AS (SELECT TOP (1000000) Number = ROW_NUMBER() OVER(ORDER BY t1.number)
         FROM master..spt_values t1
              CROSS JOIN master..spt_values t2)
INSERT INTO #Table2
SELECT DISTINCT 
       Key1
     , Key2
     , T2Key3
FROM #Table1
     CROSS APPLY (SELECT TOP (Key1*10) 
                         T2Key3 = Number
                  FROM Numbers
                  ORDER BY Number) size;

-- Table2 (same Key1, Key2 values; smallest number of distinct third Key)
WITH Numbers
     AS (SELECT TOP (1000000) Number = ROW_NUMBER() OVER(ORDER BY t1.number)
         FROM master..spt_values t1
              CROSS JOIN master..spt_values t2)
INSERT INTO #Table3
SELECT DISTINCT 
       Key1
     , Key2
     , T3Key3
FROM #Table1
     CROSS APPLY (SELECT TOP (Key1) 
                         T3Key3 = Number
                  FROM Numbers
                  ORDER BY Number) size;


DROP TABLE IF EXISTS #a;
SELECT col = 1 
INTO #a
FROM #Table1 t1
     JOIN #Table2 t2
       ON t1.Key1 = t2.Key1
          AND t1.Key2 = t2.Key2
WHERE t1.Key1 = 1;

DROP TABLE IF EXISTS #b;
SELECT col = 1 
INTO #b
FROM #Table1 t1
     JOIN #Table2 t2
       ON t1.Key1 = t2.Key1
          AND t1.Key2 = t2.Key2
     JOIN #Table3 t3
       ON t1.Key1 = t3.Key1
          AND t1.Key2 = t3.Key2
WHERE t1.Key1 = 1;

답변:


5

명확하게 말하면, 옵티마이 저는 이미 다 대다 조인이라는 것을 알고 있습니다. 병합 조인을 강제로 수행하고 예상 계획을 보면 조인이 다 대일 수 있는지 알려주는 조인 연산자의 속성을 볼 수 있습니다. 여기서 해결해야 할 문제는 카디널리티 추정치를 높이는 것입니다. 따라서 제외 된 쿼리 부분에 대해보다 효율적인 쿼리 계획을 얻을 수 있습니다.

나는 결과를 가하고 시도 할 것이라는 점을 우선에서 가입 Object3Object5임시 테이블에. 게시 한 계획의 경우 51393 행의 단일 열이므로 tempdb의 공간을 거의 차지하지 않습니다. 임시 테이블에서 전체 통계를 수집 할 수 있으며 그 자체만으로도 정확한 최종 카디널리티 추정치를 얻는 데 충분할 수 있습니다. 전체 통계를 수집 Object1하면 도움이 될 수 있습니다. 카디널리티 추정치는 계획에서 오른쪽에서 왼쪽으로 이동할 때 종종 악화됩니다.

그래도 작동하지 않으면 ENABLE_QUERY_OPTIMIZER_HOTFIXES데이터베이스 또는 서버 수준에서 쿼리 힌트를 활성화하지 않은 경우 쿼리 힌트를 시도 할 수 있습니다 . Microsoft는 해당 설정 뒤에 SQL Server 2016의 계획 영향을받는 성능 수정 프로그램을 잠급니다. 그중 일부는 카디널리티 추정과 관련이 있으므로 운이 좋을 것이고 수정 중 하나가 쿼리에 도움이 될 것입니다. FORCE_LEGACY_CARDINALITY_ESTIMATION쿼리 힌트 와 함께 레거시 카디널리티 추정기를 사용해 볼 수도 있습니다 . 특정 데이터 세트는 기존 CE로 더 나은 추정치를 얻을 수 있습니다.

최후의 수단으로 Adam Machanic의 MANY()기능을 사용하여 원하는 요소에 따라 카디널리티 추정치를 수동으로 늘릴 수 있습니다 . 나는 다른 대답으로 그것에 대해 이야기 하지만 링크가 죽은 것처럼 보입니다. 관심이 있으시면 뭔가 파헤쳐 볼 수 있습니다.


Adam의 make_parallel기능은 문제를 완화하는 데 도움이됩니다. 을 살펴 보겠습니다 many. 꽤 심한 반창고처럼 보입니다.
Steven Hibble

2

SQL Server 통계에는 통계 개체의 선행 열에 대한 히스토그램 만 포함됩니다. 따라서에 대한 막대 그래프 값을 제공 Key2하지만로 표시된 행 사이에만 필터링 된 통계를 작성할 수 있습니다 Key1 = 1. 각 테이블에서 이러한 필터링 된 통계를 작성하면 추정값이 수정되고 테스트 쿼리에 대해 예상되는 동작이 발생합니다. 각각의 새 조인은 최종 카디널리티 추정값에 영향을 미치지 않습니다 (SQL 2016 SP1 및 SQL 2017 모두에서 확인 됨).

-- Note: Add "WITH FULLSCAN" to each if you want a perfect 20,000 row estimate
CREATE STATISTICS st_#Table1 ON #Table1 (Key2) WHERE Key1 = 1
CREATE STATISTICS st_#Table2 ON #Table2 (Key2) WHERE Key1 = 1
CREATE STATISTICS st_#Table3 ON #Table3 (Key2) WHERE Key1 = 1

이러한 필터링 된 통계가 없으면 SQL Server는 조인의 카디널리티를 추정하기 위해보다 휴리스틱 기반 방식을 사용합니다. SQL 서버가 사용하는 휴리스틱 일부의 좋은 높은 수준의 설명이 포함되어 백서 다음 은 SQL Server 2014 리티 견적과 쿼리 계획을 최적화 .

예를 들어 USE HINT('ASSUME_JOIN_PREDICATE_DEPENDS_ON_FILTERS')쿼리에 힌트를 추가하면 조인 포함 휴리스틱이 변경되어 Key1조건 자와 Key2조인 조건 자간에 독립성이 아니라 일부 상관 관계를 가정 하므로 쿼리에 도움이 될 수 있습니다. 최종 테스트 쿼리의 경우이 힌트는 카디널리티 추정값을에서 (으) 1,175로 증가 7,551시키지만 20,000필터링 된 통계로 생성 된 올바른 행 추정치보다 여전히 약간 부끄럽습니다 .

비슷한 상황에서 우리가 사용한 또 다른 방법은 데이터의 관련 하위 집합을 #temp 테이블로 추출하는 것입니다. 특히 최신 버전의 SQL Server 가 더 이상 #temp 테이블을 디스크에 쓰지 않기 때문에이 방법으로 좋은 결과를 얻었습니다. 다 대다 조인에 대한 설명은 사례의 각 개별 #temp 테이블이 상대적으로 작거나 (최종 결과 집합보다 작음)이 방법을 사용하는 것이 좋습니다.

DROP TABLE IF EXISTS #Table1_extract, #Table2_extract, #Table3_extract, #c
-- Extract only the subset of rows that match the filter predicate
-- (Or better yet, extract only the subset of columns you need!)
SELECT * INTO #Table1_extract FROM #Table1 WHERE Key1 = 1
SELECT * INTO #Table2_extract FROM #Table2 WHERE Key1 = 1
SELECT * INTO #Table3_extract FROM #Table3 WHERE Key1 = 1
-- Now perform the join on those extracts, removing the filter predicate
SELECT col = 1
INTO #c 
FROM #Table1_extract t1
JOIN #Table2_extract t2
    ON t1.Key2 = t2.Key2
JOIN #Table3_extract t3
    ON t1.Key2 = t3.Key2

필터링 된 통계를 광범위하게 사용하지만 Key1각 테이블의 값당 하나씩 통계를 만듭니다 . 이제 수천 개가 있습니다.
Steven Hibble

2
@StevenHibble 수천 개의 필터링 된 통계로 인해 관리가 어려울 수 있다는 점이 좋습니다. 또한 계획 컴파일 시간에 부정적인 영향을 미치는 것으로 나타났습니다. 사용 사례에 맞지 않을 수도 있지만 여러 번 성공적으로 사용한 다른 #temp 테이블 접근 방식도 추가했습니다.
Geoff Patterson

-1

도달 범위. 시도 이외의 실제 근거는 없습니다.

SELECT 1
FROM Table1 t1
     JOIN Table2 t2
       ON t1.Key2 = t2.Key2
      AND t1.Key1 = 1
      AND t2.Key1 = 1
     JOIN Table3 t3
       ON t2.Key2 = t3.Key2
      AND t3.Key1 = 1;
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.