VARCHAR(MAX)列がインデックスに含まれている場合、値全体が常にインデックスページに格納されますか?


12

私はこの質問に触発されて、好奇心からこれを求めています。

VARCHAR(MAX)8000バイトを超える値は行に格納されず、個別のLOBページに格納されることがわかっています。その後、そのような値を持つ行を取得するには、2つ以上の論理IO操作が必要です(基本的に、理論的に必要な場合よりも1つ多い)。

リンクされた質問に示されているように、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);

次に、VARCHAR(MAX)列に8000バイトの値を持つ行をいくつか挿入し、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;

今、私はロブの読み取りを見る:

スキャンカウント1、論理読み取り20、物理読み取り0、先読み読み取り0、LOB論理読み取り5080、LOB物理読み取り0、LOB先読み読み取り0。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.