SQL Server가 인덱스를 무시하는 이유는 무엇입니까?


16

CustPassMaster16 개의 열 이있는 테이블 이 있는데 그 중 하나는 CustNum varchar(8)이며 인덱스를 만들었습니다 IX_dbo_CustPassMaster_CustNum. SELECT진술서를 실행할 때 :

SELECT * FROM dbo.CustPassMaster WHERE CustNum = '12345678'

인덱스를 완전히 무시합니다. 이 CustDataMaster방법은 더 많은 열 (55) 이있는 다른 테이블이 있으므로 혼란 스럽습니다 CustNum varchar(8). IX_dbo_CustDataMaster_CustNum이 테이블 의이 열 ( )에 색인을 생성하고 실제로 동일한 쿼리를 사용합니다.

SELECT * FROM dbo.CustDataMaster WHERE CustNum = '12345678'

그리고 내가 만든 색인을 사용합니다.

이것 뒤에 특별한 이유가 있습니까? 왜에서 색인을 사용 CustDataMaster하지만 의 색인을 사용 하지 CustPassMaster않습니까? 열 수가 적기 때문입니까?

첫 번째 쿼리는 66 개의 행을 반환합니다. 두 번째는 1 행이 반환됩니다.

또한 추가 참고 사항 : CustPassMaster4991 개의 레코드 CustDataMaster가 있고 5376 개의 레코드가 있습니다. 이것이 색인을 무시하는 이유일까요? CustPassMaster또한 동일한 CustNum값 을 가진 중복 레코드가 있습니다 . 이것이 또 다른 요인입니까?

나는이 주장을 두 쿼리의 실제 실행 계획 결과에 근거하고 있습니다.

다음은 CustPassMaster(사용되지 않은 인덱스 가있는) DDL입니다 .

CREATE TABLE dbo.CustPassMaster(
    [CustNum] [varchar](8) NOT NULL,
    [Username] [char](15) NOT NULL,
    [Password] [char](15) NOT NULL,
    /* more columns here */
    [VBTerminator] [varchar](1) NOT NULL
) ON [PRIMARY]

CREATE NONCLUSTERED INDEX [IX_dbo_CustPassMaster_CustNum] ON dbo.CustPassMaster
(
    [CustNum] ASC
) WITH (PAD_INDEX = OFF
    , STATISTICS_NORECOMPUTE = OFF
    , SORT_IN_TEMPDB = OFF
    , DROP_EXISTING = OFF
    , ONLINE = OFF
    , ALLOW_ROW_LOCKS = ON
    , ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

그리고 DDL CustDataMaster(나는 관련이없는 많은 필드를 생략했습니다) :

CREATE TABLE dbo.CustDataMaster(
    [CustNum] [varchar](8) NOT NULL,
    /* more columns here */
    [VBTerminator] [varchar](1) NOT NULL
) ON [PRIMARY]

CREATE NONCLUSTERED INDEX [IX_dbo_CustDataMaster_CustNum] ON dbo.CustDataMaster
(
    [CustNum] ASC
)WITH (PAD_INDEX = OFF
    , STATISTICS_NORECOMPUTE = OFF
    , SORT_IN_TEMPDB = OFF
    , DROP_EXISTING = OFF
    , ONLINE = OFF
    , ALLOW_ROW_LOCKS = ON
    , ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

해당 테이블 중 하나에 클러스터 된 인덱스가없고 하나의 비 클러스터형 인덱스 만 있습니다.

데이터 유형이 저장되는 데이터 유형과 완전히 일치하지 않는다는 사실을 무시하십시오. 이 필드는 IBM AS / 400 DB2 데이터베이스의 백업이며 호환 가능한 데이터 유형입니다. (이 백업 데이터베이스를 정확히 동일한 쿼리로 쿼리하고 정확히 동일한 결과를 얻을 수 있어야 합니다.)

이 데이터는 명령문 에만 사용됩니다 SELECT. 백업 응용 프로그램이 AS / 400에서 데이터를 복사하는 경우를 제외하고는 그에 대해 INSERT/ UPDATE/ DELETE문을 수행하지 않습니다 .


NonClustered에서 Clustered 로의 전환점에 대한이 기사를 읽어 볼 가치가 있습니다. sqlskills.com/blogs/kimberly/the-tipping-point-query-answers
Mark Sinkinson

3
이것이 차이점입니다. 첫 번째 쿼리에서 인덱스를 사용한 경우 65 번의 조회를 수행해야합니다. 이것은 비싸다. 두 번째 쿼리는 하나만 수행하면됩니다.
Aaron Bertrand

답변:


18

일반적으로 인덱스는 기본 테이블을 직접 사용하는 것보다 인덱스를 사용하는 것이 더 편리한 것으로 간주되는 경우 SQL Server에서 사용됩니다.

비용 기반 옵티마이 저는 실제로 해당 인덱스를 사용하는 것이 더 비싸다고 생각할 것입니다. SELECT *그냥 하는 대신 인덱스를 사용하는 것을 볼 수 있습니다 SELECT T1Col1.

당신이 때 SELECT *당신은 SQL Server를 말하고있다 테이블의 모든 열을 반환합니다. 해당 열을 반환하려면 SQL Server WHERE 테이블 자체 (클러스터형 인덱스 또는 힙)에서 명령문 조건 과 일치하는 행의 페이지를 읽어야합니다 . SQL Server는 아마도 테이블에서 나머지 열을 가져 오는 데 필요한 읽기 양이 테이블을 직접 스캔 할 수 있다고 생각합니다. 실제 쿼리와 쿼리에서 사용 된 실제 실행 계획을 보는 것이 유용합니다.


3
따라서 더 분명하고 최적의 해결책은 내가 선택한 열을 제한 INCLUDE하고 색인 절에 포함시키는 것 입니까?
Der Kommissar

1
그것은 큰 차이를 만들 수 있습니다. 쿼리에서 반환 된 모든 열을 INCLUDE절에 추가하면 SQL Server에서 인덱스를 사용할 수 있습니다. 그 말을 듣고, 무엇을 최적화하려고합니까? 테이블의 평균 행 크기가 100 바이트이면 5000 행은 약 500kb의 데이터이며 시간을 투자 할 가치가 없을 것 같습니다.
Max Vernon

1
평균 행 크기는의 경우 0.30KB Table1,의 경우 0.53KB입니다 Table2. 이 모든 데이터는 AS / 400 (IBM System i)에서 가져 오며 PK는 없습니다. 사람들이 응용 프로그램이 때때로 느리다고 언급 한 후 오늘 모든 색인을 수동으로 만들었습니다.
Der Kommissar

10

인덱스를 사용하려면을 수행하기 때문에 select *SQL Server는 먼저 where 절에있는 값과 일치하는 인덱스의 각 행을 읽어야합니다. 이를 기반으로 각 행에 대한 클러스터 된 인덱스 값을 얻은 다음 클러스터 된 인덱스와 별도로 각 인덱스 값을 찾아야합니다 (= 키 조회). 값이 고유하지 않다고 말 했으므로 SQL Server는 통계를 사용하여이 키 조회를 수행해야하는 횟수를 추정합니다.

비 클러스터형 인덱스 + 키 조회를 스캔하는 데 필요한 비용 추정값이 클러스터형 인덱스 스캔을위한 비용 예상 값을 초과 할 가능성이 높기 때문에 인덱스가 무시됩니다.

set statistics io on색인 사용을 시도한 후 색인 힌트를 사용하여 색인 사용시 I / O 비용이 실제로 더 작은 지 확인할 수 있습니다. 차이가 크면 통계가 오래된 통계를 볼 수 있습니다.

또한 SQL이 실제로 정확한 값이 아닌 변수를 사용하는 경우 이는 매개 변수 스니핑으로 인해 발생할 수도 있습니다 (= 계획을 작성하는 데 사용 된 이전 값에는 테이블에 많은 행이 있음).


1

그 이유 일 수 있습니다. 옵티마이 저는 비용 기반이며 각 실행 경로에있는 '비용'에 따라 선택할 경로를 결정합니다. 가장 큰 비용은 데이터를 디스크에서 메모리로 가져 오는 것입니다. 옵티마이 저가 인덱스와 데이터를 모두 읽는 데 시간이 더 걸린다고 계산하면 인덱스를 건너 뛰기로 결정할 수 있습니다. 행이 클수록 더 많은 디스크 블록을 가져옵니다.

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