<address id="thnfp"></address>

    <address id="thnfp"><th id="thnfp"><progress id="thnfp"></progress></th></address>
    <listing id="thnfp"><nobr id="thnfp"><meter id="thnfp"></meter></nobr></listing>
    以文本方式查看主題

    -  安易免費財務軟件交流論壇  (http://m.gangyx.cn/bbs/index.asp)
    --  電腦知識交流  (http://m.gangyx.cn/bbs/list.asp?boardid=11)
    ----  SQL Server2008存儲結構之堆表、行溢出(四)  (http://m.gangyx.cn/bbs/dispbbs.asp?boardid=11&id=32404)

    --  作者:炙天
    --  發布時間:2011/1/13 8:49:28
    --  SQL Server2008存儲結構之堆表、行溢出(四)
     下面讓我們將該記錄的describle字段更新為非空值后,再看看該記錄存儲結構相應的變化。
    UPDATE testheap SET describle=\'abc\' WHERE id=1

       再次使用dbcc page(testdb,1,224,1)命令

    圖片點擊可在新窗口打開查看
     

      我們不難發現狀態A,狀態B,定長長度、定長內容和字段總數是沒有發生任何變化的。

      NULL位圖部分變成了e0即11100000,表示describle字段即第五個字段不為空了

      第一個和第二個變長列數據終止位置分別加了2個長度,這是因為當第三個變長列變更為非空后,自動添加了2個字節的第三個字段的維護字段

      第一個變長列數據終止位置從7a00變更為7c00

      第二個變長列數據終止位置從7b00變更為7d00

      新增加的第三個變長列終止位置為8000

      同時在第一、二列變長列的數據后面新增加了616263,即字符串”abc”

      還有一個最顯著的區別就是該記錄的偏移位置顯然轉到了尾部,即5F1E的位置;但很奇怪的是該記錄原來的位置上還保留著原值,并沒有刪除掉。也就是說對于該記錄而言,應該是先刪除,然后又添加了一條新紀錄,只是把指針指向了新的偏移地址而已。

      最后觀察一下記錄是如何刪除的

    DELETE FROM testheap WHERE ID IN (2,3)

      當我們對比一下刪除前后兩條記錄的信息,發現基本上原來的位置上數據沒有發生任何變化,只是原來的slot1和slot2已經不存在了。即SQL Server認為該數據已經不存在了。

    圖片點擊可在新窗口打開查看
     

          行溢出頁面

    USE TESTDB
    CREATE 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頁的指針。

    02000000010000009d7500008813 000077cc0000 0100 0000000
    溢出列類型節點類型Lob數據更新次數ID未知字段長度行溢出指針
    RowOVerFlow011973223424 50001:52343:0


             讓我們再來看一下第52343頁看一下行溢出頁的數據情況,該頁面首先是一個LOB類型的頁面,然后主要包括該字段的長度、關聯ID,和數據行;很顯然行 內數據和溢出行數據的關聯是通過一個行溢出指針和ID進行的;因此對某個數據查詢而言,首先要找到該記錄的信息,同時如果發生行溢出,還有根據該列的行溢 出指針和關聯ID,才能找到整條記錄。

    1個字節1個字節2個字節8個字節4個字節2個字節
    0800961300009d75000000000300
    狀態A狀態B字段長度IDunkown類型
    即包含行溢出 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字段的偏移地址和相應的文件號、頁面和槽號

      這個與之前的行溢出頁面是有所不同的。


    Channel