실행 계획이 INDEX를 사용하지 않고 테이블 스캔을 사용합니다.


9

인덱스 또는 테이블 스캔을 사용할 때 SQL Server는 통계를 사용하여 어느 것이 더 나은지 알 수 있습니다.

2 천만 개의 행이있는 테이블이 있습니다. (SnapshotKey, Measure) 및이 쿼리에 대한 색인이 있습니다.

select Measure, SnapshotKey, MeasureBand
from t1
where Measure = 'FinanceFICOScore'
group by Measure, SnapshotKey, MeasureBand

쿼리는 500k 개의 행을 반환합니다. 따라서 쿼리는 테이블 행의 2.5 % 만 선택합니다.

문제는 SQL Server가 가지고있는 비 클러스터형 인덱스를 사용하지 않고 대신 테이블 스캔을 사용하는 이유입니다.

통계가 업데이트됩니다.

쿼리 성능이 우수하다는 것은 말할 것도 없습니다.

테이블 스캔

테이블 스캔

강제 색인

힘 지수

테이블 / 인덱스 구조

CREATE TABLE [t1](
    [SnapshotKey] [int] NOT NULL,
    [SnapshotDt] [date] NOT NULL,
    [Measure] [nvarchar](30) NOT NULL,
    [MeasureBand] [nvarchar](30) NOT NULL,
    -- and many more fields
) ON [PRIMARY]

데이터웨어 하우스이므로 테이블에 PK가 없습니다.

CREATE NONCLUSTERED INDEX [nci_SnapshotKeyMeasure] ON [t1]
(
    [SnapshotKey] ASC,
    [Measure] ASC
)

답변:


16

많은 행을 반환하거나 행이 매우 넓은 경우 인덱스 검색이 최선의 선택이 아닐 수 있습니다. 색인이 포함되지 않으면 조회 비용이 높아질 수 있습니다. 여기 # 2를 참조하십시오 .

시나리오에서 쿼리 최적화 프로그램은 50,000 개의 개별 조회를 수행하는 것이 단일 스캔보다 더 비싸다고 추정합니다. 스캔과 탐색 사이에서 옵티마이 저의 선택 (쿼리에 필요하지만 비 클러스터형 인덱스에는없는 열에 대한 RID 조회 포함)은 각 대안 의 예상 비용 을 기반으로합니다 .

옵티마이 저는 항상 가장 저렴한 대안을 선택합니다. 당신이 보면 예상 하위 트리 비용의 두 실행 계획의 루트 노드 재산, 당신은 검사 계획은이 계획을 추구보다 낮은 추정 비용이 것을 볼 수 있습니다. 결과적으로 옵티마이 저가 스캔을 선택했습니다. 그것은 본질적으로 귀하의 질문에 대한 답변입니다.

이제 옵티마이 저가 사용하는 비용 모델은 시스템 성능 특성과 거의 일치하지 않는 가정 및 "마법 수"를 기반으로합니다. 특히, 모델에서 한 가지 가정은 메모리에 이미 필요한 데이터 나 인덱스 페이지가 없어도 쿼리가 실행되기 시작한다는 것입니다. 다른 하나는 순차적 I / O (스캔에 예상 됨)가 RID 조회에 대해 가정 된 임의 I / O 패턴보다 저렴하다는 것입니다. 다른 많은 가정과주의 사항이 있지만 여기에 자세히 설명하기에는 너무 많습니다.

그럼에도 불구하고 비용 모델 전체 는 대부분의 쿼리, 대부분의 데이터베이스 스키마, 대부분의 하드웨어 구성, 대부분의 시간에 대해 일반적으로 "충분한"계획을 생성하는 것으로 나타났습니다. 당신이 그것에 대해 생각한다면 그것은 꽤 업적입니다.

모델 제한 및 기타 요인으로 인해 옵티마이 저가 실제로 "충분히 충분하지 않은"계획을 선택하는 경우가 있습니다. "성능이 양호"하다고보고하므로 여기서는 그렇지 않습니다.


9

실제로 595,947 개의 일치하는 행이 있으며 이는 데이터의 약 3 %입니다. 따라서 조회 비용이 빠르게 증가합니다. 테이블에 페이지 당 100 개의 행이 있고 테이블 스캔에서 읽을 20 만 페이지라고 가정하십시오. 595,947 조회보다 훨씬 저렴합니다.

GROUP BY질문 의 절을 사용하면 (Measure, SnapshotKey, MeasureBand)에 복합 키를 사용하는 것이 좋습니다.

"누락 된 색인"제안을보십시오. 조회를 피하기 위해 열을 포함하도록 지시합니다. 보다 일반적으로 쿼리에서 다른 열을 참조 INCLUDE하는 경우 새 인덱스 의 키 또는 절에 있어야합니다. 그렇지 않으면 해당 값을 얻기 위해 여전히 595,947 조회를 수행해야합니다.

예를 들어, 쿼리의 경우 :

select Measure, SnapshotKey, MeasureBand, SUM(NumLoans), SUM(PrinBal)
from t1
where Measure = 'FinanceFICOScore'
group by Measure, SnapshotKey, MeasureBand

... 필요한 것 :

CREATE INDEX ixWhatever 
ON t1 (Measure, SnapshotKey, MeasureBand) 
INCLUDE (NumLoans,PrinBal);

6
  1. WHERE 조건의 필드는 색인의 선행 필드가 아닙니다.

  2. 당신은 measureNVARCHAR는 그래서 리터럴을 접두사로 정의 N: where Measure = N'FinanceFICOScore'.

에 클러스터형 인덱스 생성을 고려하십시오 SnapshotKey. 고유 한 경우 PK (및 클러스터) 일 수 있습니다. 고유하지 않은 경우 PK가 될 수 없지만 여전히 고유하지 않은 클러스터형 인덱스 일 수 있습니다. 그런 다음 비 클러스터형 인덱스는 measure열에 만 있습니다 .

그리고의 첫 번째 필드 GROUP BY도 앞선 필드 라는 점을 고려할 때 measure, 이는 또한 measure선두 필드가되는 것이 좋습니다.

실제로이 작업을 수행 Measure, SnapshotKey, MeasureBand하려면 GROUP BY절 과 일치하는 순서대로 NonClustered Index on을 대신 정의해야합니다 . 크기 현명한 만 정말 추가하고 MeasureBand클러스터되지 않은 인덱스부터 이미 기반으로 Measure하고, MeasureKey이 (아니, 이제 클러스터 된 인덱스 키와 같이 이미 인덱스에 포함되어 Measure클러스터되지 않은 인덱스에서 중복되지 않습니다).

@Rob는이 문제를 해결하기 위해 비 클러스터형 인덱스를이 세 필드로이 순서대로 정의하면되고 클러스터 된 (고유하지 않은) 인덱스를 생성 할 필요SnapshotKey 는 없다고 답변에 대해 삭제 된 의견에서 언급했습니다 . 그는 정확하지만 (더 적은 수의 필드가 작동하기를 바랐지만) 여전히 Clustered Index를 갖는 것이이 작업뿐만 아니라 대부분의 다른 작업에 도움이된다고 주장합니다.


이 답변에 대한 토론은 채팅 으로 이동 되었습니다 .
Paul White 9
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.