Если в индекс включен столбец VARCHAR (MAX), всегда ли все значение сохраняется на страницах индекса?

12

Я спрашиваю это из любопытства, будучи вдохновленным этим вопросом .

Мы знаем, что VARCHAR(MAX)значения длиннее 8000 байт хранятся не в строках, а на отдельных страницах больших объектов. Впоследствии для извлечения строки с таким значением требуются две или более логических операций ввода-вывода (по сути, теоретически потребуется еще одна операция, чем в противном случае).

Мы можем добавить VARCHAR(MAX)столбец, как INCLUDEd, к уникальному индексу, как показано в связанном вопросе. Если этот столбец имеет значения, длина которых превышает 8000 байт, будут ли такие значения по-прежнему сохраняться «встроенными» на листовых страницах индекса или они также будут перемещены на страницы больших объектов?

mustaccio
источник

Ответы:

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'); 

В индексе нет больших страниц:

╔═════════════╦════════════════════╦══════════════════════╦════════════╦══════════════╗
 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 страница большого объекта в индексе для каждой строки, которую я только что вставил:

╔═════════════╦════════════════════╦══════════════════════╦════════════╦══════════════╗
 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, логическое чтение с 0, физическое чтение с 0, чтение с опережением 0.

Если я вместо этого запросить строки с 8001 байтами:

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

Теперь я вижу, что Лоб читает:

Сканирование 1, логическое чтение 20, физическое чтение 0, чтение с опережением 0, логическое чтение 5080, физическое чтение 1, чтение с опережением 0.

Джо Оббиш
источник