인덱스를 만들 때 INCLUDE 절을 사용해야하는 이유는 무엇입니까?


431

70-433 시험을 공부하는 동안 다음 두 가지 방법 중 하나로 취재 지수를 만들 수 있음을 알게되었습니다.

CREATE INDEX idx1 ON MyTable (Col1, Col2, Col3)

-또는-

CREATE INDEX idx1 ON MyTable (Col1) INCLUDE (Col2, Col3)

INCLUDE 절이 처음입니다. INCLUDE 절을 사용하거나 사용하지 않고 포함 인덱스를 작성할지 여부를 결정하는 데 왜이를 사용하고 어떤 지침을 제안 하시겠습니까?

답변:


363

열이에 없지만 절의 WHERE/JOIN/GROUP BY/ORDER BY열 목록에만있는 경우SELECT

INCLUDE절은 인덱스 트리가 아니라 가장 낮은 / 리프 레벨에서 데이터를 추가합니다. 이것은 트리의 일부가 아니기 때문에 인덱스를 작게 만듭니다.

INCLUDE columns인덱스의 키 열이 아니므로 순서가 지정되지 않습니다. 이것은 위에서 언급했듯이 술어, 정렬 등에 실제로 유용하지 않음을 의미합니다. 그러나, 그것은 수 있습니다 당신은 키 컬럼에서 몇 행에 잔류 조회가있는 경우 유용합니다 (들)

효과적인 예제가 포함 된 다른 MSDN 기사


7
그렇다면 이것은 저렴한 인덱스 버전의 커버 인덱스를 만드는 기술입니까?
JMarsch

3
@gbn,이 문장을보다 자세하게 설명하고 include 절이 정렬에 유용하지 않은 이유를 설명하십시오. "INCLUDE 절은 인덱스 트리가 아닌 가장 낮은 / 리프 레벨에서 데이터를 추가합니다 .는 "나무의 일부가 아니기 때문에이 인덱스 작은 수
돌라 Odejayi

4
@ JMarsch : 늦은 답변에 대해 유감이지만, 그렇습니다. 정확합니다.
gbn

10
@Tola Odejayi : INCLUDE 열은 인덱스의 주요 열이 아니므로 순서가 지정되지 않습니다. 따라서 일반적으로 JOIN 또는 정렬에 유용 하지 않습니다 . 그것들은 키 칼럼이 아니기 때문에 키 칼럼과 같은 전체 B- 트리 구조에 앉아 있지 않습니다
gbn

4
이것이 가장 인정되는 답변이지만 추가 설명이 필요하다고 생각합니다. 일부 쿼리의 경우 열이 일부이고 SELECT그렇지 않은 경우에는 어떻게해야합니까? \
Chisko

215

INCLUDE를 사용하여 비 클러스터형 인덱스의 리프 수준에 하나 이상의 열을 추가합니다. 이렇게하면 쿼리를 "덮을"수 있습니다.

직원 ID, 부서 ID 및 성을 쿼리해야한다고 상상해보십시오.

SELECT EmployeeID, DepartmentID, LastName
FROM Employee
WHERE DepartmentID = 5

비 클러스터형 인덱스 (EmployeeID, DepartmentID)가있는 경우 특정 부서의 직원을 찾으면 실제 열람을 얻기 위해 "책갈피 조회"를 수행해야합니다. . 많은 직원을 찾으면 성능면에서 꽤 비쌀 수 있습니다.

색인에 해당 성을 포함시킨 경우 :

CREATE NONCLUSTERED INDEX NC_EmpDep 
  ON Employee(EmployeeID, DepartmentID)
  INCLUDE (Lastname)

그런 다음 필요한 모든 정보를 비 클러스터형 인덱스의 리프 수준에서 사용할 수 있습니다. 비 클러스터형 인덱스를 찾아 특정 부서의 직원을 찾으면 필요한 모든 정보를 얻을 수 있으며 인덱스에서 찾은 각 직원에 대한 책갈피 조회가 더 이상 필요하지 않습니다.-> 많은 시간을 절약 할 수 있습니다.

분명히 모든 비 클러스터형 인덱스에 모든 열을 포함 할 수는 없습니다. 그러나 "커버"할 하나 또는 두 개의 열만 누락 된 쿼리가있는 경우 (그리고 많이 사용되는) 쿼리를 포함하면 매우 유용 할 수 있습니다. 적절한 비 클러스터형 인덱스로


25
이 인덱스를 사용 하시겠습니까? 왜 EmployeeID인가? 키 열에 DepartmentID 만 필요합니까? 당신은 여기 권위있는 것으로 인용되었습니다 : stackoverflow.com/q/6187904/27535
gbn

3
설명은 좋지만 실제로는 사용 사례와 일치하지 않습니다. 키 열 JOIN은 쿼리 의 필터에 있어야하며 , INCLUDE검색하지만 정렬하지 않은 데이터 여야합니다.
JNK

15
우선 직원 (EmployeeID, DepartmentID) 인덱스가 DepartmentID = 5를 필터링하는 데 사용되지 않습니다. 주문이 일치하지 않기 때문에
AnandPhadke

29

이 논의는 중요한 점을 놓치고 경우 : "키가 아닌 - 열"로 포함하는 더 나은 경우 문제는 아니다 인덱스 -columns 또는 포함 -columns.

문제는 인덱스에 실제로 필요하지 않은 열을 포함시키기 위해 include-mechanism을 사용하는 것이 얼마나 비싸 습니까? (일반적으로 where-clauses의 일부는 아니지만 종종 select에 포함됨). 따라서 당신의 딜레마는 항상 :

  1. id1, id2 ... idN 에만 색인을 사용 하거나
  2. id1, id2 ... idN에 인덱스 사용 플러스 col1, col2 ... colN 포함

여기서 : id1, id2 ... idN은 제한에 자주 사용되는 열이고 col1, col2 ... colN은 자주 선택되지만 제한에 사용 되지 않는 열입니다.

(이러한 모든 열을 인덱스 키의 일부로 포함하는 옵션은 항상 바보입니다 (제한에도 사용되지 않는 한). 인덱스를 업데이트하고 정렬해야 할 때에도 인덱스를 업데이트하고 정렬해야하기 때문에 유지 관리 비용이 항상 비쌉니다. "키"는 변경되지 않았습니다).

옵션 1 또는 2를 사용 하시겠습니까?

답변 : 테이블이 거의 업데이트되지 않는 경우 (주로 삽입 / 삭제되는 경우) include 메커니즘을 사용하여 일부 "핫 컬럼"(일부 선택에 사용되지만 제한에 자주 사용 되지않음) 을 포함하는 것이 비교적 저렴합니다. 삽입 / 삭제는 어쨌든 인덱스를 업데이트 / 정렬해야하므로 인덱스를 업데이트하는 동안 몇 개의 추가 열을 저장하는 것과 관련된 추가 오버 헤드가 거의 없습니다. 오버 헤드는 인덱스에 중복 정보를 저장하는 데 사용되는 추가 메모리 및 CPU입니다.

포함 된 열로 추가하려는 열이 자주 업데이트되는 경우 (인덱스 키- 열이 업데이트 되지 않은 경우 ) 또는 열 이 너무 많은 경우 인덱스가 테이블의 복사본에 가까워지는 경우-옵션 1 사용 나는 제안 할 것이다! 또한 특정 include-column을 추가해도 성능 차이가없는 것으로 판명되면 추가하는 아이디어를 건너 뛸 수 있습니다.) 유용한 지 확인하십시오!

키에서 동일한 값 (id1, id2 ... idN) 당 평균 행 수도 중요 할 수 있습니다.

int로서 첨가 - 컬럼 경우 통지 포함 - 인덱스의 -Column가 사용된다 제한 : 같은 색인이 사용될 수만큼 긴 (인덱스 - 대 제한에 기초하여 -columns) - 다음 SQL 서버 매칭 인 테이블 자체를 고가로 사용하는 대신 인덱스 (리프 노드 값)에 대한 열 제한.


18

기본 인덱스 열은 정렬되지만 포함 된 열은 정렬되지 않습니다. 이를 통해 인덱스 유지 관리에 필요한 리소스를 절약하면서도 포함 된 열에 데이터를 제공하여 쿼리를 처리 할 수 ​​있습니다. 따라서 쿼리를 다루려면 검색 기준을 지정하여 행을 인덱스의 정렬 된 열로 찾은 다음 검색되지 않은 데이터가있는 정렬되지 않은 추가 열을 "포함"할 수 있습니다. 인덱스 유지 관리에서 정렬 및 조각화 양을 줄이는 데 도움이됩니다.


7

그 이유 (지수의 잎 수준에있는 데이터 포함)가 잘 설명되어 있습니다. 이것에 대해 두 가지 동요를하는 이유는 쿼리를 실행할 때 추가 열이 포함되어 있지 않으면 (SQL 2005의 새로운 기능) SQL Server가 추가 열을 가져 오기 위해 클러스터형 인덱스로 이동해야하기 때문입니다 새로운 데이터 페이지가 메모리에로드 될 때 SQL Server 서비스, 디스크 및 메모리 (구체적으로 특정 버퍼)에 더 많은로드를 추가하여 버퍼 캐시에서 더 자주 필요한 다른 데이터를 푸시 할 수 있습니다.


실제로 적은 메모리를 사용하고 있음을 증명하는 방법이 있습니까? 그것도 나도 기대하는 것이지만 직장에서 이것에 대해 약간의 정체를
느끼고있다

힙 또는 클러스터형 인덱스에서 페이지를 메모리로로드해야한다는 것을 감안할 때 인덱스 페이지는 메모리에 중복 데이터를 넣는다는 것을 의미합니다. 구체적으로 측정하는 방법은 없습니다.
mrdenny

5

이미 주어진 답변에서 보지 못한 추가 고려 사항은 포함 된 열이 varchar (max)와 같은 인덱스 키 열로 허용되지 않는 데이터 유형 일 수 있다는 것입니다.

이를 통해 해당 인덱스를 포함 인덱스에 포함시킬 수 있습니다. 최근에 SELECT에 많은 열이 있고 유용한 인덱스가있는 nHibernate 생성 쿼리를 제공하기 위해이 작업을 수행해야했습니다.


3

INCLUDE에서 해당 열이 필요하지 않은 경우 키 열보다 선호하는 한 가지 이유 는 문서입니다. 이는 미래에 진화하는 색인을 훨씬 더 쉽게 만듭니다.

귀하의 예를 고려할 때 :

CREATE INDEX idx1 ON MyTable (Col1) INCLUDE (Col2, Col3)

쿼리가 다음과 같은 경우 해당 인덱스가 가장 좋습니다.

SELECT col2, col3
  FROM MyTable
 WHERE col1 = ...

물론 INCLUDE주요 부분 에 열을 추가하여 이점을 얻을 수 있다면 열을 넣지 마십시오 . 다음 쿼리는 모두 col2인덱스 키의 열을 선호합니다 .

SELECT col2, col3
  FROM MyTable
 WHERE col1 = ...
   AND col2 = ...
SELECT TOP 1 col2, col3
  FROM MyTable
 WHERE col1 = ...
 ORDER BY col2

의이이 가정 해 봅시다 없는 경우 우리는이 col2에서 INCLUDE인덱스의 나무 부분에있는의 단지 어떤 이점이 없기 때문에 절.

몇 년 빨리 감기.

이 쿼리를 조정해야합니다.

SELECT TOP 1 col2
  FROM MyTable
 WHERE col1 = ...
 ORDER BY another_col

해당 쿼리를 최적화하려면 다음 색인이 좋습니다.

CREATE INDEX idx1 ON MyTable (Col1, another_col) INCLUDE (Col2)

해당 테이블에있는 인덱스를 이미 확인한 경우 이전 인덱스가 여전히있을 수 있습니다.

CREATE INDEX idx1 ON MyTable (Col1) INCLUDE (Col2, Col3)

지금 당신은 알고 Col2Col3인덱스 트리의 일부가 아닌 때문에 읽기 인덱스 범위를 좁힐이나 행을 주문하는 데 사용되지 않습니다. another_column인덱스의 핵심 부분 끝에 추가 하는 것이 오히려 안전합니다 (after col1). 아무것도 깨뜨릴 위험이 거의 없습니다 :

DROP INDEX idx1 ON MyTable;
CREATE INDEX idx1 ON MyTable (Col1, another_col) INCLUDE (Col2, Col3);

이 지수는 더 커질 위험이 있지만 새로운 지수를 도입하는 것보다 기존 지수를 확장하는 것이 일반적으로 좋습니다.

이없는 인덱스가 있으면 바로 뒤에 INCLUDE추가하여 어떤 쿼리를 중단할지 알 수 없었습니다 .another_colCol1

CREATE INDEX idx1 ON MyTable (Col1, Col2, Col3)

another_col사이에 추가하면 어떻게됩니까 ? 다른 쿼리에 어려움이 있습니까?Col1Col2

테이블에서 열을 가져 오지 않도록 열을 추가하면 다른 열과INCLUDE 키 열의 다른 이점 이 있습니다 . 그러나 설명서 측면이 가장 중요하다고 생각합니다.

질문에 대답하려면 :

INCLUDE 절을 사용하거나 사용하지 않고 포함 인덱스를 작성할지 여부를 결정할 때 어떤 지침을 제안 하시겠습니까?

테이블을 방문하지 않고 인덱스에서 해당 열을 사용할 수 있도록하기 위해 인덱스에 열을 추가하는 경우 해당 열을 INCLUDE절에 넣으십시오 .

인덱스 키에 열을 추가하면 추가 인덱스 이점을 얻을 수있는 경우 (예 : order by읽기 인덱스 범위를 좁힐 수 있기 때문에) 키에 추가하십시오.

이에 대한 자세한 내용은 여기를 참조하십시오.

https://use-the-index-luke.com/blog/2019-04/include-columns-in-btree-indexes


2

인덱스 정의에 인라인 된 모든 열의 총 크기에는 제한이 있습니다. 그럼에도 불구하고, 그렇게 넓은 인덱스를 만들 필요는 없었습니다. 나에게 가장 큰 장점은 특정 순서로 정의 할 필요가 없으므로 열을 포함하는 하나의 인덱스로 더 많은 쿼리를 처리 할 수 ​​있다는 것입니다. 는 인덱스 내의 인덱스로 생각하십시오. 한 가지 예는 StoreID (여기서 StoreID는 선택성이 낮으며 각 상점이 많은 고객과 연관되어 있음을 의미 함)와 고객 인구 통계 데이터 (LastName, FirstName, DOB)입니다. , FirstName, DOB)에서 StoreID 및 LastName을 알고있는 고객 만 효율적으로 검색 할 수 있습니다.

반면, StoreID에 인덱스를 정의하고 LastName, FirstName, DOB 열을 포함하면 본질적으로 StoreID에 대해 두 개의 seek-index 술어를 수행 한 다음 포함 된 열에 대해 술어를 탐색 할 수 있습니다. 이렇게하면 StoreID로 시작하는 한 가능한 모든 검색 순열을 다룰 수 있습니다.

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