【SQL SERVER 2005頁面存儲2之--特殊數據類型在頁面中的存儲】

上一篇講了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=1LOB頁面所在的文件號

 再後面的->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)textimageXMLUDT、通過MAX標識的變長數據類型、行版本以及cursortable變量。顯然CURSOR及表變量是不能定義爲表的列類型。通過聯機幫助我們可以看到sql_variantMS爲支持上層產品的半結構化數據而設計的。我們把這種類型應用概念類型的表中,比如爲了便於擴展而增加一些事先不知道類型的列,那麼我們就可以把這些列定義爲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 轉成十進制=插入的值

 

--第二條                

/*

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=第一個表示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.其他數據類型無額外字節

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章