선택도가 높고 선택도가 낮은 복합 인덱스 순서의 필드 순서


11

30 억 개가 넘는 행이있는 SQL Server 테이블이 있습니다. 내 쿼리 중 하나가 너무 오래 걸리므로 최적화를 고려하고 있습니다. 쿼리는 다음과 같습니다.

SELECT [Enroll_Date]
      ,Count(*) AS [Record #]
      ,Count(Distinct UserID) AS [User #]
  FROM UserTable
  GROUP BY [Enroll_Date]

[Enroll_Date]는 가능한 값이 50 개 미만인 낮은 선택도 열이며, UserID 열은 2 억개 이상의 고유 값을 가진 높은 선택도 열입니다. 내 연구에 따르면이 두 열에 클러스터되지 않은 복합 인덱스를 만들어야하며 이론적으로 높은 선택성 열이 첫 번째 열이어야한다고 생각합니다. 그러나 필자의 경우 확실하지 않습니다 .group by 절에서 낮은 선택성 열을 사용하고 있기 때문에 효과가 있습니까?

이 테이블에는 클러스터형 인덱스가 없습니다.


실제 실행 계획 xml을 게시 할 수 있습니까 (pastbin을 사용하고 여기에 연결하십시오)? 어떤 버전의 SQL Server를 사용하고 있습니까?
Kin Shah

3
선택성이 높은 열이 먼저있는 인덱스는 특정 쿼리에 쓸모가 없습니다.
ypercubeᵀᴹ

높은 선택성 열을 인덱스의 첫 번째 키 열 (일반적으로)로 사용하는 것이 가장 좋습니다. 이 시나리오에서는 짐작했듯이 전혀 도움이되지 않습니다. 두 개의 인덱스가 필요할 수 있습니다! enroll_date를 먼저 사용하고 user_id를 두 번째로 사용하면 어떻게됩니까?
paulbarbin

답변:


12

@AaronBertrand의 솔루션에 대한 대안으로 (인덱싱 된 뷰를 만들 수 없거나 원하지 않는 경우)에 인덱스를 만드는 것이 좋습니다 (Enroll_Date, UserID). 이 유형의 질문이 테이블에서 매우 일반적인 경우 아마도 클러스터형 인덱스 여야합니다.

일반적으로 높은 선택성 인덱스를 일반적인 "모범 사례"로 권장하지는 않지만 쿼리에 최고의 성능을 제공 할 인덱스를 확인하십시오.

인덱스를 사용 (Enroll_Date, UserID)하면 쿼리에 Stream Aggregates를 사용하여 최적화 된 비 차단 쿼리 계획을 제공 할 수 있습니다.

스트림 집계 쿼리 계획

이 문맥에서 "비 차단"이란 쿼리가 많은 양의 데이터 (예 : 정렬 또는 해시 집계와 같은)를 버퍼링 할 필요가 없음을 의미합니다. 즉, (a) 즉시 행을 반환하기 시작합니다. b) 실제 작업 메모리를 소비하지 않습니다.


웃긴, 4 초 간격으로 같은 대답.
usr

11

아론의 대답은 훌륭한 해결책입니다. 나는 당신이 그 접근법을 원하지 않는다고 가정하면서 질문에 대답 할 것입니다.

게시 한 쿼리는 일반적으로에서를 먼저 그룹화 (Enroll_Date, UserID)한 다음 다시 시작하여 실행됩니다 (Enroll_Date). 이 최적화는 SQL Server 2012에 새로 도입되었으며 단일 경우에 적용됩니다 COUNT DISTINCT.

특정 순서의 두 열에 (Enroll_Date, UserID)대한 인덱스는 인덱스 스캔을 두 개의 연속 스트림 집계로 퍼뜨리는 효율적인 계획을 세우기에 충분합니다. 반대 순서는 해당 계획을 활성화하지 않습니다.

따라서 order를 사용하십시오 (Enroll_Date, UserID). 당신은 여기서 선택의 여지가 없습니다.


5 초 간격으로 동일한 솔루션입니다. 잘 연주했습니다. :)
Daniel Hutmacher

@DanielHutmacher OMG, 우리는 세 번째 게시물과 거의 일치할까요?! 당신에게 +1! 어떻게 같은 대답을 지지 할 수 없습니까?
usr

매트릭스의 글리치. :)
Daniel Hutmacher

대단히 감사합니다. 색인을 작성 중이며 완료 후 개선 사항을 게시합니다. 서버 버전은 AWS의 Microsoft SQL Server 2008 R2이지만 여전히 유일한 선택이라고 생각합니다.
Thinkinger

@Thinkinger 당신이 Aarons의 접근 방식을 받아들이지 않는다면 당신은 어려운 선택을 할 것입니다 :)
usr

11

인덱싱 된 뷰의 이상적인 시나리오처럼 들리므로 쿼리 시간 대신 쓰기 시간에 계산 및 집계 비용을 지불 할 수 있습니다.

CREATE VIEW dbo.MyIndexedView
WITH SCHEMABINDING
AS 
  SELECT Enroll_Date, UserID, RawCount = COUNT_BIG(*)
  FROM dbo.UserTable
  GROUP BY Enroll_Date, UserID;
GO

CREATE UNIQUE CLUSTERED INDEX CIX_miv ON dbo.MyIndexedView(Enroll_Date, UserID);

생성하는 데 시간이 걸리고 기본 테이블의 인덱스와 마찬가지로 모든 DML 작업에서 유지 관리가 필요합니다.

이제이 뷰에 대한 쿼리는 매우 유사합니다. 이제 뷰의 각 행은 고유 한 사용자 / 날짜 콤보를 나타내므로 그림은 단일 COUNT (*)로 계산할 수 있으며 기본 테이블의 총 행 수는 다음과 같습니다. 이미 부분적으로 집계되었으므로 이제 날짜 당 SUM을 사용하여 추가해야합니다.

SELECT Enroll_Date, 
  [Record #] = SUM(RawCount),
  [User #] = COUNT(*)
FROM dbo.MyIndexedView WITH (NOEXPAND)
GROUP BY Enroll_Date; 

thisthis를 기억 한 후 NOEXPAND 힌트를 추가 했습니다 .

각 쿼리마다 정확히 한 명의 사용자가있는 드문 경우 (이 경우 동일한 양의 데이터가있는 경우를 제외하고)이 쿼리가 현재 쿼리보다 빠르다는 것을 의심 할 여지없이 알 수 있습니다 우리가 알고있는 열은 기본 테이블의 인덱스에서 유일한 열입니다. 읽기 시간에 이러한 성능 향상이 워크로드의 쓰기 부분에 영향을 미치는 추가 작업의 가치가 있는지 여부는 우리가 알 수없는 것입니다. 트레이드 오프를 측정하기 위해 테스트해야합니다 (인덱스 없음).

그리고 잘 정의 된 특정 범위 (예 : 현재 분기 또는 연도)에 대해 Enroll_Date에 대해 동일한 공통 WHERE 절을 자주 사용하는 경우 일치하는 필터링 된 인덱스를 추가하여 I / O를 훨씬 더 줄일 수 있습니다 (그러나 항상 거래).

기본 테이블에 클러스터형 인덱스를 넣는 것도 고려할 수 있습니다. 이것은 힙에서 이익을 얻는 매우 드문 사용 사례 중 하나가 아닌 것 같습니다.


방금 IT 부서에 확인했는데 이런 종류의 견해를 만들 수없는 것 같습니다. 그러나 여전히 당신의 충고를 이해하십시오. 그리고 그것은 그것을 이용할 수있는 다른 사람들을 도울 것입니다.
Thinkinger

1
IT 부서는 인덱싱 된 뷰와 기본 테이블의 추가 또는 다른 인덱스간에 상당한 차이가 있다고 생각합니까? 많은 사람들이 인덱싱 된 뷰에 대해 잘못된 생각을 가지고 있기 때문에 전투 적이 지 않고 호기심을 가지지 않습니다. 나는 그것들을 테이블에 대한 더 마른 클러스터형 인덱스로 생각하지만 행 수가 적습니다.
Aaron Bertrand

@Thinkinger는 인덱싱 된 뷰가 EE 전용이 아닙니다. 인덱스 된 뷰 일치 는 EE 전용입니다. NOEXPAND를 사용하여 직접 타겟팅 할 수 있습니다.
usr
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.