
再次使用dbcc page(testdb,1,224,1)命令
我們不難發現狀態A,狀態B,定長長度、定長內容和字段總數是沒有發生任何變化的。
NULL位圖部分變成了e0即11100000,表示describle字段即第五個字段不為空了
第一個和第二個變長列數據終止位置分別加了2個長度,這是因為當第三個變長列變更為非空后,自動添加了2個字節的第三個字段的維護字段
第一個變長列數據終止位置從7a00變更為7c00
第二個變長列數據終止位置從7b00變更為7d00
新增加的第三個變長列終止位置為8000
同時在第一、二列變長列的數據后面新增加了616263,即字符串”abc”
還有一個最顯著的區別就是該記錄的偏移位置顯然轉到了尾部,即5F1E的位置;但很奇怪的是該記錄原來的位置上還保留著原值,并沒有刪除掉。也就是說對于該記錄而言,應該是先刪除,然后又添加了一條新紀錄,只是把指針指向了新的偏移地址而已。
最后觀察一下記錄是如何刪除的
當我們對比一下刪除前后兩條記錄的信息,發現基本上原來的位置上數據沒有發生任何變化,只是原來的slot1和slot2已經不存在了。即SQL Server認為該數據已經不存在了。
行溢出頁面
USE TESTDBCREATE TABLE testOverFlow
(
ID INT IDENTITY(1,1),
NAME1 VARCHAR(5000),
NAME2 VARCHAR(5000)
)
INSERT INTO testOverFlow (NAME1,NAME2)
SELECT REPLICATE('A',5000),REPLICATE('AB',2500)
UNION
SELECT REPLICATE('B',5000),REPLICATE('BA',2500)
SELECT * FROM testOverFlow
SELECT type_desc
total_pages,used_pages,data_pages,
testdb.dbo.f_get_page(first_page) first_page_address,
testdb.dbo.f_get_page(root_page) root_address,
testdb.dbo.f_get_page(first_iam_page) IAM_address
FROM sys.system_internals_allocation_units
WHERE container_id IN (SELECT partition_id FROM sys.partitions
WHERE object_id in (SELECT object_id FROM sys.objects
WHERE name IN ('testoverflow')))
DBCC TRACEON(3604)
DBCC PAGE(testdb,1,54242,2) --行內數據
DBCC PAGE(testdb,1,52343,2) --行遷移數據
--同時我們也可以通過dbcc ind獲取所有數據頁面地址,然后進行頁面信息顯示
TRUNCATE TABLE tablepage;
INSERT INTO tablepage EXEC ('DBCC IND(testdb,testOverFlow,1)');
SELECT
PagePID,IAMPID,ObjectID,IndexID,Pagetype,IndexLevel,
NextPagePID,PrevPagePID
FROM tablepage
在NAME2字段之前和普通的行記錄信息是一致的,我們只從NAME2字段開始就可以了。NAME2字段在NAME1字段之后,保存了以下內容,即改列的溢出列類型、節點類型、數據庫更新次數、字段長度、指向OVERFLOW頁的指針。
0200 | 0000 | 0100 | 00009d75 | 0000 | 8813 0000 | 77cc0000 0100 0000000 |
溢出列類型 | 節點類型 | Lob數據更新次數 | ID | 未知 | 字段長度 | 行溢出指針 |
RowOVerFlow | 0 | 1 | 1973223424 | 5000 | 1:52343:0 |
讓我們再來看一下第52343頁看一下行溢出頁的數據情況,該頁面首先是一個LOB類型的頁面,然后主要包括該字段的長度、關聯ID,和數據行;很顯然行 內數據和溢出行數據的關聯是通過一個行溢出指針和ID進行的;因此對某個數據查詢而言,首先要找到該記錄的信息,同時如果發生行溢出,還有根據該列的行溢 出指針和關聯ID,才能找到整條記錄。
1個字節 | 1個字節 | 2個字節 | 8個字節 | 4個字節 | 2個字節 |
08 | 00 | 9613 | 00009d75 | 00000000 | 0300 |
狀態A | 狀態B | 字段長度 | ID | unkown | 類型 |
即包含行溢出 | 5014(同變長字段) | 1973223424 | 未知 | lob數據行 |
LOB頁面
從SQL Server 2005版本以后中,新增加了大值數據類型varchar(max)、nvarchar(max)、varbinary(max)。大值數據類型最多可以存儲2^30-1個字節的數據。
從行為上來講這幾個數據類型和之前的數據類型 varchar、nvarchar 和 varbinary 相同。
按照微軟的說法是用這個數據類型來代替之前的text、ntext 和 image 數據類型,它們之間的對應關系為:
varchar(max)-------text;
nvarchar(max)-----ntext;
varbinary(max)----image
對大值數據類型的操作更類似于之前的varchar和varbinary之后,因此用法上也比之前的text和image比靈活和便宜。同時觸發器也可以直接引用大值數據類型;而之前的text和image是不行的。
因此varchar(max)與varchar(n)和text有著千絲萬縷的聯系。對于varbinary(max)也一樣。
因為之前我們已經觀察過varchar(n)的行為,那么讓我們看看這個新的varchar(max)與varchar(n)、text到底有什么不同。
CREATE TABLE testVARCHARMAX
(
ID INT IDENTITY(1,1),
name VARCHAR(20),
remark VARCHAR(MAX)
)
CREATE TABLE testTEXT
(
ID INT IDENTITY(1,1),
name VARCHAR(20),
remark TEXT
)
INSERT INTO testVARCHARMAX (name,remark)
SELECT REPLICATE('A',20),REPLICATE('AB',2500)
UNION
SELECT REPLICATE('B',20),REPLICATE('BA',2500)
INSERT INTO testTEXT (name,remark)
SELECT REPLICATE('A',20),REPLICATE('AB',2500)
UNION
SELECT REPLICATE('B',20),REPLICATE('BA',2500)
SELECT c.name,a.type_desc
total_pages,used_pages,data_pages,
testdb.dbo.f_get_page(first_page) first_page_address,
testdb.dbo.f_get_page(root_page) root_address,
testdb.dbo.f_get_page(first_iam_page) IAM_address
FROM sys.system_internals_allocation_units a,sys.partitions b,sys.objects c
WHERE a.container_id=b.partition_id and b.object_id=c.object_id
AND c.name in ('testVARCHARMAX','testTEXT')
運行結果如下:
我們很容易發現兩者的共同之處,就是兩個表都包括LOB_DATA數據類型的分配單元,但是testVARCHARMAX表的LOB_DATA并沒有分 配頁面,而testTEXT表卻分配了3個頁面;同時testVARCHARMAX表比testTEXT表多了一個數據頁面,這是怎么回事呢?
讓我們首先看看testVARCHARMAX表的第217個數據頁面
讓我們通過Internals Viewer插件看一下對該記錄的解讀
與之前的堆表的介紹相比,基本上我們可以看到與varchar(n)的存儲結構式完全一致的,在此就不多做敘述了。
那么testTEXT表為什么會使用到LOB類型頁面呢?我們使用dbcc page命令查看一下。
運行dbcc page(testDB,1,222),我們從第96個字節開始閱讀。
從這個角度,我們看到222頁面類似于前面所講到的行溢出頁面,即在222頁面保留了一個指向行溢出頁面的指針
運行dbcc page(testDB,1,220,2),我們從第96個字節開始閱讀。
實際上我們從name字段內容之后閱讀就可以了,即0000d1 07000000 00dc0000 00010001 00
是不是有點像縮略版的行溢出信息?
既然有行溢出指針,必然有行溢出頁面,那我們再看看行溢出頁面的數據頁,即220頁面。實際上我們用dbcc page(testdb,1,220,3)閱讀該頁的信息更簡明一些。
很明顯slot 0記錄了第一條記錄remark字段的長度、數據類型和內容。
Slot 1,slot 2分別為兩個指針,記錄了remark字段的偏移地址和相應的文件號、頁面和槽號
這個與之前的行溢出頁面是有所不同的。