VARCHAR (MAX) 열이 인덱스에 포함 된 경우 전체 값이 항상 인덱스 페이지에 저장됩니까?


12

나는 이 질문 에서 영감을 얻어 호기심에서 이것을 요구하고있다 .

우리가 알고있는VARCHAR(MAX)8000 바이트 행에 저장되지 않습니다보다 값이 이상하지만, 별도의 LOB 페이지입니다. 결과적으로 그러한 값으로 행을 검색하려면 둘 이상의 논리적 IO 조작이 필요합니다 (실제로 이론적으로 필요한 것보다 하나 이상 필요).

링크 된 질문에서 설명한대로 고유 인덱스에 VARCHAR(MAX)열을 INCLUDEd 로 추가 할 수 있습니다 . 이 열의 길이가 8000 바이트를 초과하는 경우 해당 값은 여전히 ​​색인 리프 페이지에 "인라인"으로 저장되거나 LOB 페이지로 이동됩니까?

답변:


16

8000 바이트를 초과하는 값은 "인라인"으로 저장할 수 없습니다. LOB 페이지에 저장됩니다. sys.dm_db_index_physical_stats에서 이를 확인할 수 있습니다 . 간단한 테이블로 시작하십시오.

USE tempdb;

DROP TABLE IF EXISTS #LOB_FOR_ME;

CREATE TABLE #LOB_FOR_ME (
ID BIGINT,
MAX_VERNON_WAS_HERE VARCHAR(MAX) 
);

CREATE INDEX IX ON #LOB_FOR_ME (ID) INCLUDE (MAX_VERNON_WAS_HERE);

이제 열의 값이 8000 바이트 인 값을 가진 행을 삽입 VARCHAR(MAX)하고 DMF를 확인하십시오.

USE tempdb;

INSERT INTO #LOB_FOR_ME
SELECT 1, REPLICATE('Z', 8000)
FROM master..spt_values;

SELECT index_level, index_type_desc, alloc_unit_type_desc, page_count, record_count
FROM sys.dm_db_index_physical_stats(DB_ID(), OBJECT_ID('#LOB_FOR_ME'), 2, NULL , 'DETAILED'); 

색인에 LOB 페이지가 없습니다.

╔═════════════╦════════════════════╦══════════════════════╦════════════╦══════════════╗
 index_level   index_type_desc    alloc_unit_type_desc  page_count  record_count 
╠═════════════╬════════════════════╬══════════════════════╬════════════╬══════════════╣
           0  NONCLUSTERED INDEX  IN_ROW_DATA                 2540          2540 
           1  NONCLUSTERED INDEX  IN_ROW_DATA                   18          2540 
           2  NONCLUSTERED INDEX  IN_ROW_DATA                    1            18 
╚═════════════╩════════════════════╩══════════════════════╩════════════╩══════════════╝

그러나 8001 바이트를 취하는 값을 가진 행을 추가하면 :

USE tempdb;

INSERT INTO #LOB_FOR_ME
SELECT 2, REPLICATE(CAST('Z' AS VARCHAR(MAX)), 8001)
FROM master..spt_values;

SELECT index_level, index_type_desc, alloc_unit_type_desc, page_count, record_count
FROM sys.dm_db_index_physical_stats(DB_ID(), OBJECT_ID('#LOB_FOR_ME'), 2, NULL , 'DETAILED'); 

이제 방금 삽입 한 모든 행에 대해 색인에 1 개의 LOB 페이지가 있습니다.

╔═════════════╦════════════════════╦══════════════════════╦════════════╦══════════════╗
 index_level   index_type_desc    alloc_unit_type_desc  page_count  record_count 
╠═════════════╬════════════════════╬══════════════════════╬════════════╬══════════════╣
           0  NONCLUSTERED INDEX  IN_ROW_DATA                 2556          5080 
           1  NONCLUSTERED INDEX  IN_ROW_DATA                   18          2556 
           2  NONCLUSTERED INDEX  IN_ROW_DATA                    1            18 
           0  NONCLUSTERED INDEX  LOB_DATA                    2540          2540 
╚═════════════╩════════════════════╩══════════════════════╩════════════╩══════════════╝

당신은 또한 SET STATISTICS IO ON;올바른 쿼리 와 함께 이것을 볼 수 있습니다 . 8000 바이트의 행만 보는 다음 쿼리를 고려하십시오.

SELECT SUM(LEN(MAX_VERNON_WAS_HERE))
FROM #LOB_FOR_ME
WHERE ID = 1;

실행 결과 :

스캔 횟수 1, 논리적 읽기 2560, 물리적 읽기 0, 미리 읽기 0, lob 논리적 읽기 0, lob 물리적 읽기 0, lob 미리 읽기 0

대신 8001 바이트로 행을 쿼리하면 :

SELECT SUM(LEN(MAX_VERNON_WAS_HERE))
FROM #LOB_FOR_ME
WHERE ID = 2;

이제 lob reads를 봅니다.

스캔 카운트 1, 논리적 읽기 20, 물리적 읽기 0, 미리 읽기 0, lob 논리적 읽기 5080, lob 물리적 읽기 0, lob 미리 읽기 0.

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