上一篇講了SQL SERVER 2005頁面存儲中的一般頁面存儲和讀取方法http://blog.csdn.net/feixianxxx/archive/2010/03/17/5390317.aspx
這一篇來講講特殊的數據類型在頁面中的存儲,這裏分析2個特殊數據類型:LOB類型和SQL_VARIANT類型
參考文獻--技術內幕系列+MSDN
LOB數據類型
當表中存在LOB類型數據(TEXT/NTEXT/IMAGE)時候:
默認的情況下(TEXT IN ROWS選項是關閉的)數據是不會存儲在DATA頁面上的.它是存儲在屬於自己的LOB頁面上的,在數據頁面只留下字節的指針;
在設置表選項來改變這個存儲機制時候,他有可能會存儲在DATA頁面上。
那麼我們來通過例子具體看下LOB類型數據的存儲
/*----------------------------------------------------------------------
*auther:Poofly
*date:2010.3.14
*VERSION:
Microsoft SQL Server 2008 (RTM) - 10.0.1600.22 (Intel X86)
Jul 9 2008 14:43:34
Copyright (c) 1988-2008 Microsoft Corporation
Enterprise Evaluation Edition on Windows NT 6.1 <X86> (Build 7600: )
*轉載請註明出處
*更多精彩內容,請進http://blog.csdn.net/feixianxxx
------------------------------------------------------------------------*/
--建表(表源技術內幕)
if OBJECT_ID('Hastext') is not null
drop table Hastext
GO
create table Hastext
(
COL1 CHAR(3) NOT NULL,
COL2 VARCHAR(5) NOT NULL,
COL3 TEXT NOT NULL,--此處TEXT字段
COL4 VARCHAR(20) NOT NULL
)
--插入測試數據
INSERT Hastext
SELECT 'AAA','BBB',REPLICATE('X',250),'CCC'
--檢查頁面分佈和類型
SELECT convert(char(7), object_name(object_id)) AS name,
partition_id, partition_number AS pnum, rows,
allocation_unit_id AS au_id, convert(char(17),type_desc) as page_type_desc,
total_pages AS pages
FROM sys.partitions p JOIN sys.allocation_units a
ON p.partition_id = a.container_id
WHERE object_id=object_id('dbo.Hastext');
--有IN_ROW_PAGE lOB_DATA 分配單元各有頁,其中一頁爲IAM頁。
--查找頁面具體文件號和頁面號
dbcc ind(tempdb,Hastext ,-1)
/*
PageFID PagePID iam_chain_type PageType
1 127 In-row data 10
1 126 In-row data 1 ---data page
1 174 LOB data 10
1 173 LOB data 3 ----LOB page
*/
--查看頁面信息
dbcc traceon(3604) --此追蹤可以顯示頁面輸出結果
dbcc page(tempdb,1,126,1) --查看數據頁的頁面信息
/*
Slot 0, Offset 0x60, Length 40, DumpStyle BYTE
Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP VARIABLE_COLUMNS
Record Size = 40
Memory Dump @0x63D8C060
00000000: 30000700 41414104 00800300 15002580 ?...AAA.......%.
00000010: 28004242 420000e5 07000000 00ad0000 ?.BBB...........
00000020: 00010001 00434343 ?8224 ??8224 ??8224 ??8224 ??8224 ??8224 ??8224 ??8224 ??8224 ??....CCC PS:這裏右邊部分每一個小點表示一個字節
*/
--分析幾個值:
我在這裏分析下這個含有LOB數據類型的行的page讀取方法 你可以對照我在上一偏文章<關於一般頁面存儲的研究>http://blog.csdn.net/feixianxxx/archive/2010/03/17/5390317.aspx的行結構進行對照 依次分解讀取
30=>00110000 從左往右看第一個表示有變長列 第二個表示存在NULL位圖
00=>00000000 未啓用
0700=>0000000000001011 頁位移量爲 1+1+2+3(col1 char(3)),說明真正數據從第字節開始
414141=>010000010100000101000001 轉成十進制 再轉成ASICC碼 值爲A 即COL1 ‘AAA’
0400=>0000000000000100 一共列
08=>10000000 表裏有列 最後位爲 表示都不爲NULL
0300=>0000000000000011 變長列爲列 這裏的TEXT列也算成變成列
1500=>0000000000010101 第一列變長列的終止位置
2580->1000000000100101 第二列變長列(TEXT)的終止位置
2800->0000000000101000=40 該列外爲最後的可變列終止位置 正好就等於LENGTH 40
424242=>010000100100001001000010 轉成十進制 再轉成ASICC碼 值爲B 即COL1 ‘BBB’
0000e5 07000000 00ad0000 00010001 00==>這裏的個字節是文本(TEXT)在這個DATA頁面的指針
其中值ad00 0000是從這個指針留下來指向頁面號的字節> Oxad00 0000
(Oxad00==>0000000010011101 轉成十進制爲 恰好是我們的LOB頁面的頁面號),
後面的->00000000 00000001=1是LOB頁面所在的文件號
再後面的->00000000 00000001=1是該條記錄在LOB頁面的Slot號.
434343==>轉成十進制 再轉成ASICC碼 值爲C 即COL1 ‘CCC’
我們開啓表中的TEXT IN ROW 看看發生了什麼
exec sp_tableoption Hastext,'text in row',500
--查看DATA頁面
dbcc page(tempdb,1,126,1)
--發現結果跟不開啓前一樣那是因爲要使在LOB頁面上的數據轉移到DATA頁面必須更新文本
update Hastext
set COL3=REPLICATE('k',250)
--再次查看
dbcc page(tempdb,1,126,1)
/*
00000000: 30000700 41414104 00800300 15000f01 ?...AAA.........
00000010: 12014242 426b6b6b 6b6b6b6b 6b6b6b6b ?.BBBkkkkkkkkkkk
00000020: 6b6b6b6b 6b6b6b6b 6b6b6b6b 6b6b6b6b ?kkkkkkkkkkkkkkk
00000030: 6b6b6b6b 6b6b6b6b 6b6b6b6b 6b6b6b6b ?kkkkkkkkkkkkkkk
00000040: 6b6b6b6b 6b6b6b6b 6b6b6b6b 6b6b6b6b ?kkkkkkkkkkkkkkk
。。。
00000100: 6b6b6b6b 6b6b6b6b 6b6b6b6b 6b6b6b43 ?kkkkkkkkkkkkkkC
00000110: 4343?8224 ??8224 ??8224 ??8224 ??8224 ??8224 ??8224 ??8224 ??8224 ??8224 ??8224 ??8224 ??8224 ??8224 ??8224 ??8224 ??C
*/
我們可以清楚看到 6b6b 也就是KK都進入了DATA頁面。。
--如果我們是改變該選項的大小上限呢
exec sp_tableoption Hastext,'text in row',50
--再次查看
dbcc page(tempdb,1,126,1)
/*
Slot 0, Offset 0x60, Length 48, DumpStyle BYTE
00000000: 30000700 41414104 00800300 15002d80 ?...AAA.......-.
00000010: 30004242 42040000 62010000 00366b00 ?.BBB...b....6k.
00000020: 00fa0000 00940000 00010000 00434343 ?............CCC
*/
變化:LOB數據消失在DATA頁面
變化:Length 48 而不是 . 這裏多出來的個字節是什麼呢?我們來對比一下這行數據和上面一開始的數據,不同點在這:
0000e5 07000000 00ad0000 00010001 00==》字節的指針
040000 62010000 00366b00 00fa0000 00940000 00010000 00 ==》這是一個根結構
這裏說明當我們修改選項上限大小,對於那些大小和上限不符合的(這裏指大於上限)的LOB數據,在DATA頁面存儲的不是一個字節的指針,而是一個至少字節的根結構
ps:增加選項的上限的大小 效果是開啓一樣的 需要更新文本才能轉移數據
--現在我們來關閉TEXT IN ROW 看看發生了什麼
exec sp_tableoption Hastext,'text in row',0
--再次查看
dbcc page(tempdb,1,126,1)
--我們可以看到DATA頁面的LOB數據又消失了,length 也回到了,也就是存字節的指針
結論:
LOB類型數據在數據頁面的存儲分種情況:
1.當表的'text in row'選項關閉的時候,我們的LOB數據在數據頁面不會保存任何具體數據,只留下個字節指針;
2.當表的'text in row'選項開啓的時候,如果行的LOB數據大小大於選項上限,會留下一個至少字節的指針(B-樹的根結構).
如果行的LOB數據大小小於選項上限,在行大小能限制在的前提下,可以將LOB數據存儲在數據頁面,否則只能把LOB數據推到LOB頁面
SQL_VARIANT數據類型
/*----------------------------------------------------------------------
*auther:poofly
*date:2010.3.14
*VERSION:
Microsoft SQL Server 2008 (RTM) - 10.0.1600.22 (Intel X86)
Jul 9 2008 14:43:34
Copyright (c) 1988-2008 Microsoft Corporation
Enterprise Evaluation Edition on Windows NT 6.1 <X86> (Build 7600: )
*轉載請註明出處
*更多精彩內容,請進http://blog.csdn.net/feixianxxx
------------------------------------------------------------------------*/
sql_variant數據類型是一種特殊的數據類型, 它可以允許存儲任何類型的數據,除了幾下幾種情況:(n)text、image、XML、UDT、通過MAX標識的變長數據類型、行版本以及cursor和table變量。顯然CURSOR及表變量是不能定義爲表的列類型。通過聯機幫助我們可以看到sql_variant是MS爲支持上層產品的半結構化數據而設計的。我們把這種類型應用概念類型的表中,比如爲了便於擴展而增加一些事先不知道類型的列,那麼我們就可以把這些列定義爲sql_variant,這樣一來概念表就轉化爲緊湊的真實表。
sql_variant 內部存儲總是被認爲變長的,其實SQLSERVER還是知道具體的類型的,因爲存儲結構的第一個字節總是表示該記錄真實的基礎數據類型,下面我們研究頁面內容 時再細說這個字節。它的最大長度可以是 8016 個字節。實際基類型值的最大長度是 8,000 個字節。由於對這個類型的使用不是本文重點 所以不再闡述.
下面通過一個簡單的測試來說明sql_variant類型的數據存儲情況。
--建表
create table variant
(
col1 int,
col2 sql_variant
)
--插入數據
insert variant values(1,1)
insert variant values(2,100000000000)
insert variant values(3,'asasa')
insert variant values(4,CURRENT_TIMESTAMP)
go
--查看頁面:只存在IN_ROW_DATA頁面
SELECT convert(char(7), object_name(object_id)) AS name,
partition_id, partition_number AS pnum, rows,
allocation_unit_id AS au_id, convert(char(17),type_desc) as page_type_desc,
total_pages AS pages
FROM sys.partitions p JOIN sys.allocation_units a
ON p.partition_id = a.container_id
WHERE object_id=object_id('dbo.variant');
go
--取得文件號頁面號
dbcc ind(poofly,'variant' ,-1)--5:42
go
--查看讀取PAGE
dbcc traceon(3604) --此追蹤可以顯示頁面輸出結果
dbcc page(poofly,5,42,1)
--我們一條條數據來看
--第一條
/*
Slot 0, Offset 0x60, Length 21, DumpStyle BYTE
Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP VARIABLE_COLUMNS
Record Size = 21
Memory Dump @0x6394C060
00000000: 30000800 01000000 02000001 00150038 ?..............8
00000010: 01010000 00? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?.....
*/
前面個字節我不再分析(每行也是一樣的) 上一篇LOB存儲已經解釋過了。
1500->0000000000010101=5+16=21 表示第一列變長列終止的位置 表只有一個變長列所以正好等於記錄長度
38->00111000=8+48=56 該字節表示你的SQL_VARIANT列的數據類型,爲int類型
這裏的數據類型的值就是sys.types視圖中的system_type_id列的值
select name,system_type_id from sys.types
--類型很多列出幾個需要的
/*
name system_type_id
int 56
datetime 61
numeric 108
varchar 167
*/
01->00000001 表示SQL_variant 版本的字節SQL2005/8裏面總是
ps:由於是INT類型所以版本後面沒有多餘的字節(除了最後的真實數據),個別數據還是會有字節的看下面
010000 00->00...0001 轉成十進制=1 插入的值
--第二條
/*
Slot 1, Offset 0x75, Length 28, DumpStyle BYTE
Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP VARIABLE_COLUMNS
Record Size = 28
Memory Dump @0x6394C075
00000000: 30000800 02000000 02000001 001c006c ?0..............l
00000010: 010c0001 00e87648 17000000 ??????????......vH....
*/
注意看這裏的幾點(特別是版本號後面Numberic類型對應的個字節):
1c00->0000000000011100=28
6c->01101100 轉成十進制就是再對應system_type_id 就是numberic 類型這裏不用bigint類型的原因是這樣存省空間
01->slq_variant版本號
0c00->00001100=12 00000000=0 第一個表示NUMBERIC的精度表示刻度 100000000000 不就是numberic(12,0)麼?
01 00e87648 17000000->真實值
--第三條
/*
Slot 2, Offset 0x91, Length 28, DumpStyle BYTE
Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP VARIABLE_COLUMNS
Record Size = 28
Memory Dump @0x6394C091
00000000: 30000800 03000000 02000001 001c00a7 ?...............
00000010: 01401f24 d0000061 73617361 ? ?? ?? ?? ?? ?.@.$...asasa */
注意看這裏的幾點(特別是版本號後面varchar類型對應的個字節):
1c00->0000000000011100=28
a7->10010111=167 再對應system_type_id 就是varchar 類型
01->slq_variant版本號
401f->0001111101000000=8000 字符串類型版本號後面帶的個字節表示該類型的最大長度
24 d00000->00000000000000001101000000100100 排序規則ID
61 73617361->0110001=97='a' 1=01110011=115='s' 'asasa'長度五個字節.
--第三條
/*
Slot 3, Offset 0xad, Length 25, DumpStyle BYTE
Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP VARIABLE_COLUMNS
Record Size = 25
Memory Dump @0x6394C0AD
00000000: 30000800 04000000 02000001 0019003d ?..............=
00000010: 01754cdc 00399d00 00? ?? ?? ?? ?? ?? ?? ?? ??uL..9...
*/
1900->0000000000011001=25
3d->00111101=61 再對應system_type_id 就是datetime 類型
01->版本號
754cdc 00399d00 00->datetime類型個字節真實數據current_timestamp
ps:關於版本號後面各個數據類型哪些帶字節哪些不帶如下:
1.numeric/decimal:1個字節表示精度,個字節表示刻度
2.字符串:2個字節表示類型對應的最大長度,接下來的個字節表示排序規則的ID
3.binary/varbinary:個字節表示最長長度
4.其他數據類型無額外字節