Jeśli kolumna VARCHAR (MAX) jest zawarta w indeksie, czy cała wartość jest zawsze przechowywana na stronach indeksowych?


12

Pytam o to z ciekawości, inspirując się tym pytaniem .

Wiemy, że VARCHAR(MAX)wartości dłuższe niż 8000 bajtów nie są przechowywane w wierszach, ale na osobnych stronach LOB. Późniejsze pobranie wiersza o takiej wartości wymaga dwóch lub więcej logicznych operacji IO (zasadniczo jedna więcej niż byłoby teoretycznie konieczne).

Możemy dodać VARCHAR(MAX)kolumnę, jako INCLUDEd, do unikalnego indeksu, jak pokazano w połączonym pytaniu. Jeśli ta kolumna ma wartości przekraczające 8000 bajtów, czy takie wartości byłyby nadal przechowywane „w linii” na stronach liści indeksu, czy też zostałyby przeniesione na strony LOB?

Odpowiedzi:


16

Wartości przekraczające 8000 bajtów nie mogą być przechowywane „w linii”. Są one przechowywane na stronach LOB. Możesz to zobaczyć za pomocą sys.dm_db_index_physical_stats . Zacznij od prostego stołu:

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

Teraz wstaw kilka wierszy z wartościami, które zajmują 8000 bajtów dla VARCHAR(MAX)kolumny i sprawdź 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'); 

W indeksie nie ma stron 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 ║
╚═════════════╩════════════════════╩══════════════════════╩════════════╩══════════════╝

Ale jeśli dodam wiersze z wartościami, które zajmują 8001 bajtów:

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

Teraz mam 1 stronę LOB w indeksie dla każdego właśnie wstawionego wiersza:

╔═════════════╦════════════════════╦══════════════════════╦════════════╦══════════════╗
║ 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 ║
╚═════════════╩════════════════════╩══════════════════════╩════════════╩══════════════╝

Możesz to również zobaczyć za SET STATISTICS IO ON;pomocą odpowiedniego zapytania. Rozważ następujące zapytanie, które dotyczy tylko wierszy o 8000 bajtach:

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

Wyniki po wykonaniu:

Liczba skanów 1, logiczne odczyty 2560, fizyczne odczyty 0, odczyt z wyprzedzeniem 0, lob logiczne odczyty 0, lob fizyczne odczyty 0, lob odczyty z wyprzedzeniem 0.

Jeśli zamiast tego sprawdzę wiersze z 8001 bajtami:

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

Teraz widzę, że LOB czyta:

Liczba skanów 1, logiczne odczyty 20, fizyczne odczyty 0, odczyt z wyprzedzeniem 0, lob logiczne odczyty 5080, lob fizyczne odczyty 0, lob odczyty z wyprzedzeniem 0.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.