SQL SERVER 2008 新数据库类型hierarchyid

 

SQL SERVER 2008 新数据库类型 hierarchyid

      

    SQL SERVER 2008 引入了新的 hierarchyid 数据类型 , 可以用它来做本地存储并且在树层次

结构中管理其位置 . 只用这个函数能简洁地表示层次结构 中的位置 . 该函数提供的一些内置的函

数方法可以操作和遍历层次结构 , 使得存储和查询分层数据更为容易 , 而不需要像那样通过

CTE 递归来获得 .

 

    该类型其实是一个 CLR 自定义数据类型依次打开 : 数据库 -> 系统数据库 ->master-> 可编程性

-> 类型 -> 系统数据类型 ->CLR 数据类型 ->hierarchyid, 可以看到该数据类型 .

   

    hierarchyid 有关的一些函数主要有 :

   

    GetAncestor :取 得某一个级别的祖先

    GetDescendant :取 得某一个级别的子代

    GetLevel :取 得级别

    GetRoot :取得根

    IsDescendantOf :判 断某个节点是否为某个节点的子代

    Parse :将字符串转换为 hierarchyid 。该字符串的格式通常都是 /1/ 这样的

    Read Read 从传入的 BinaryReader 读取 SqlHierarchyId 的二进制表示形式,并将

           SqlHierarchyId 对象 设置为该值。不能使用 Transact-SQL 调用 Read 。请改

           为使用 CAST CONVERT

    GetReparentedValue :可 以用来移动节点(或者子树)

    ToString :将 hierarchyid 转换为字符串,与 parse 正好相反

    Write : SqlHierarchyId 的二进制表示形式写出到传入的 BinaryWriter 中。无法通

           过使用 Transact-SQL 来调用 Write 。请改为使用 CAST CONVERT

 

 

    以下就该新类型做一些演示

*/

 

    USE TESTDB

    GO

   

    CREATE TABLE TEST (

           [PHASE] hierarchyid NOT NULL,

           LVL AS [PHASE] . GetLevel (),

           USERID INT NOT NULL,

           USERNAME VARCHAR ( 50) NOT NULL

           )

   

    -- 接着插入一个根

   

    INSERT TEST ( [PHASE] , USERID , USERNAME )

    VALUES ( '/' , 1001, ' 东升 ' ) --'/' 被用来表示层次的根 , 会自动转换成二进制格式

   

   

    SELECT * FROM TEST

   

/*  结果

 

    PHASE  LVL    USERID     USERNAME

    ------ ------ ---------  ----------

    0x     0      1001       东升

 

    (1 行受影响 )

   

    查询后应该发现 '/' 被重新定义成 x 十六进制值 .

   

    使用斜杠字符来表示层次路径 , 一个表示的是根 , 用斜杠分隔的整数值来组成连续的层 次 .

   

   

    插入小弟

*/

    INSERT TEST ( [PHASE] , USERID , USERNAME )

    VALUES ( '/1/' , 1002, ' 土豆 ' )

   

    INSERT TEST ( [PHASE] , USERID , USERNAME )

    VALUES ( '/2/' , 1003, ' 红太狼 ' )

    -- 以上条数据时同级别的

   

   

    SELECT * FROM TEST

/*

    PHASE      LVL    USERID      USERNAME

    -------    ------ ---------  -----------

    0x         0      1001        东升

    0x58       1      1002        土豆

    0x68       1      1003        红太狼

 

    (3 行受影响 )

   

*/

   

   

    -- 同样可以使用 GetDescendant 方法来填充 [PHASE]

    DECLARE     @PARENTPHASE hierarchyid , @Child1 hierarchyid

    SELECT @PARENTPHASE = CONVERT ( hierarchyid , '/1/' )

   

    INSERT TEST ( [PHASE] , USERID , USERNAME )

    VALUES ( @PARENTPHASE . GetDescendant (NULL,NULL), 1004, ' 土豆小弟 1 ' )

   

    SELECT @Child1 = CAST ( '/1/1/' AS hierarchyid )

    INSERT TEST ( [PHASE] , USERID , USERNAME )

    VALUES ( @PARENTPHASE . GetDescendant ( @Child1 ,NULL), 1005, ' 土豆小弟 2 ' )

   

    SELECT [PHASE] . ToString () AS [PHASE] , LVL , USERID , USERNAME FROM TEST

    ORDER BY [PHASE]

   

/*

    PHASE      LVL    USERID      USERNAME

    ---------- ------ ---------  ---------

    /          0      1001        东升

    /1/        1      1002        土豆

    /1/1/      2      1004        土豆小弟 1

    /1/2/      2      1005        土豆小弟 2

    /2/        1      1003        红太狼

 

    (5 行受影响 )

 

    查询中的中 ToString 方法可以显示 hierarchyid 类型的字符串表示 , 而不是十六进制值 .

    该方法下面会经常用到 .

   

   

    下面演示一下上面提到的函数方法 ( 注意方法的调用要注意大小写 ):

   

   

1 GetAncestor: child.GetAncestor ( n ) 返回指定的祖先 .

       用于测试输出中的每个节点是否将 当前节点作为指定级别的祖先。

       如果传递的数字大于 GetLevel() ,则返回 NULL

       如果传递的是负数,则引发异常

 

*/

 

    SELECT PHASE . GetAncestor ( 2). ToString () AS Ancestor

           -- 把对应的二级祖先的层次显示为字符串

    , PHASE . ToString () as CURPHASE

    , USERNAME

    FROM TEST

    WHERE LVL >= 2 -- 去除级别小于的行           

 

/*

    Ancestor       CURPHASE   USERNAME

    -----------   ---------  ----------

    /              /1/1/      土豆小弟 1

    /              /1/2/      土豆小弟 2

 

    (2 行受影响 )

   

    注意 GetAncestor(0) 返回的是节点本身

 

 

2 GetDescendant: parent.GetDescendant ( child1 , child2 ) 返回子节点

    child1:NULL 或当 前节点的子节点的 hierarchyid

    child2:NULL 或当 前节点的子节点的 hierarchyid

 

    如果父级为 NULL ,则返回 NULL

    如果父级不为 NULL ,而 child1 child2 NULL ,则返回父级的子级。

    如果父级和 child1 不为 NULL ,而 child2 NULL ,则返回一个大于 child1 的父级的子级。

    如果父级和 child2 不为 NULL ,而 child1 NULL ,则返回一个小于 child2 的父级的子级。

    如果父级、 child1 child2 都不为 NULL ,则返回一个大于 child1 且小 于 child2 的父级的子级。

    如果 child1 不为 NULL 且不 是父级的子级,则引发异常。

    如果 child2 不为 NULL 且不 是父级的子级,则引发异常。

    如果 child1 >= child2 ,则引发异常。

*/

 

    -- 插入一行作为最低级别的后代节点 , 上面插入的时候就运用的这个特点

    -- 不含参数的 GetDescendant 方法 插入新行,以将新行节点指定为 /1/1/

    -- 上面执行过 , 此处不执行

    DECLARE     @PARENTPHASE hierarchyid

    SELECT @PARENTPHASE = CONVERT ( hierarchyid , '/1/' )

    INSERT TEST ( [PHASE] , USERID , USERNAME )

    VALUES ( @PARENTPHASE . GetDescendant (NULL,NULL), 1004, ' 土豆小弟 1 ' )

   

    -- 插入一行作为较高级别的后代节点

    -- 使用带有 Child1 参数 的 GetDescendant 方法插入新行

    -- 指定新行的节点将紧跟 '/1/1/' 节点,成为 /1/2/

    -- 上面执行过 , 此处不执行

    DECLARE     @PARENTPHASE hierarchyid , @Child1 hierarchyid

    SELECT @PARENTPHASE = CONVERT ( hierarchyid , '/1/' )

    SELECT @Child1 = CAST ( '/1/1/' AS hierarchyid )

    INSERT TEST ( [PHASE] , USERID , USERNAME )

    VALUES ( @PARENTPHASE . GetDescendant ( @Child1 ,NULL), 1005, ' 土豆小弟 2 ' )

   

    -- 在两个现有节点之间插入一行

    -- 同时使用 child1 参数 和 child2 参数指定新行的节点将成为节点 /1/1.1/

    DECLARE     @PARENTPHASE hierarchyid , @Child1 hierarchyid , @Child2 hierarchyid

    SELECT @PARENTPHASE = CONVERT ( hierarchyid , '/1/' )

    SELECT @Child1 = CAST ( '/1/1/' AS hierarchyid )

    SELECT @Child2 = CAST ( '/1/2/' AS hierarchyid )

    INSERT TEST ( [PHASE] , USERID , USERNAME )

    VALUES ( @PARENTPHASE . GetDescendant ( @Child1 , @Child2 ), 1006, ' 土豆小弟 ' )

   

    SELECT [PHASE] . ToString () AS [PHASE] , lvl , userid , username

    FROM   test ORDER BY [PHASE]

   

    /*

    PHASE      lvl    userid      username

    ------     ------ ---------- ------------

    /          0      1001        东升

    /1/        1      1002        土豆

    /1/1.1/    2      1006        土豆小弟 3

    /1/1/      2      1004        土豆小弟 1

    /1/2/      2      1005        土豆小弟 2

    /2/        1      1003        红太狼

 

    (6 行受影响 )

   

    节点 /1/1.1/ 高于节点 /1/1/ 低于节点 /1/2/, 但是属于同一级别

   

   

3 GetLevel (): node.GetLevel () 返回一个表示节点在树中的深度的 整数。

*/

 

    -- 将层次结构级别返回为列 , 本例的测试表建立的计算列用的就是这个特性 .

   

    SELECT PHASE . GetLevel () AS lvl1 , LVL FROM TEST

    /*

    lvl1   LVL

    ------ ------

    0      0

    1      1

    1      1

    2      2

    2      2

    2      2

 

    (6 行受影响 )

    */

   

    -- 返回层次结构级别的所有成员

   

    SELECT * FROM TEST WHERE PHASE . GetLevel ()= 1

   

    /*

    PHASE  LVL    USERID      USERNAME

    ------ ------ ---------- -----------

    0x58   1      1002        土豆

    0x68   1      1003        红太狼

 

    (2 行受影响 )

   

    同样可以用 PHASE.GetLevel()=0 返回根节点

   

   

   

   

4 IsDescendantOf child.IsDescendantOf(parent) 判断某个节点是否为某个节点的子代

*/

    DECLARE @parent hierarchyid

    SELECT @parent = PHASE FROM TEST WHERE USERNAME = ' 土豆 '

    SELECT * FROM TEST

    WHERE PHASE . IsDescendantOf ( @parent )= 1

   

    /*

    PHASE  LVL    USERID      USERNAME

    ------ ------ -----------   -----------

    0x58   1      1002        土豆

    0x5AC0 2      1004        土豆小弟 1

    0x5B40 2      1005        土豆小弟 2

    0x5B16 2      1006        土豆小弟 3

 

    (4 行受影响 )

   

    父级被视为其本身的后代 , 所以土豆的子代中包含土豆

 

 

5 GetRoot: 返回层次结构树的根节点 , 是一个静态方法。

*/

 

    -- 获得根节点

   

    SELECT PHASE . ToString () AS ROOT , *

    FROM TEST

    WHERE PHASE = hierarchyid :: GetRoot ()

 

 

    /*

    ROOT       PHASE      LVL    USERID      USERNAME

    -------    -------    -----  ---------  ----------

    /          0x         0      1001        东升

 

    (1 行受影响 )

   

 

6 Parse: hierarchyid 的规范字符串表示形式转换为 hierarchyid 值。当发

         生从字符串类型到 hierarchyid 的转换时,将隐式调用 Parse 。作用

         ToString 相反。 Parse() 是静态方法。

  ToString: hierarchyid 转换为字符串,与 parse 正好相反

*/

 

 

    DECLARE @String AS nvarchar ( 100), @hierarchyid AS hierarchyid

    SET @String = '/1/1/'

    SET @hierarchyid = 0x5AC0

 

    SELECT hierarchyid :: Parse ( @String ) AS hierarchyidvalue ,

           @hierarchyid . ToString () AS Stringvalue

          

    SELECT CONVERT ( hierarchyid , @String )

           , CONVERT ( nvarchar ( 100), @hierarchyid )

   

   

    /*

    hierarchyidvalue  Stringvalue

    ----------------  -------------

    0x5AC0            /1/1/

 

    (1 行受影响 )

   

    可以看到结果二者的作用刚好是相 反的 . 其实测试后他们可以发现 CONVERT

    可以实现他们的效果 . 如果 Parse 收到的值不是 hierarchyid 的有效字符

    串表示形式,则会引发异常 , 例如尾部有空格

   

    SELECT hierarchyid::Parse('/1/1 ')

   

    ----------------------------------------------------------------------------------

    消息 6522 ,级别 16 ,状态 2 ,第 1

    在执行用户定义例程或聚合 "hierarchyid" 期间出现 .NET Framework 错误 :

    Microsoft.SqlServer.Types.HierarchyIdException: 24001: SqlHierarchyId.Parse 失败

    ,因为输入字符串 '/1/1 ' 不是 SqlHierarchyId 节点 的有效字符串表示形式。

    Microsoft.SqlServer.Types.HierarchyIdException:

    Microsoft.SqlServer.Types.SqlHierarchyId.Parse(SqlString input)

 

 

 

7 GetReparentedValue : 节点或者是子树的移动

   

   

    1) 节点的移动

*/

 

    -- 查询一下前后的变化

 

    DECLARE @ActionUser hierarchyid , @OldParent hierarchyid , @NewParent hierarchyid

    SELECT @ActionUser = PHASE FROM TEST

      WHERE USERNAME = ' 土豆小弟 3 ' --/1/1.1/

    SELECT @OldParent = PHASE FROM TEST

      WHERE USERNAME = ' 土豆 '   --  /1/1/

    SELECT @NewParent = PHASE FROM TEST

      WHERE USERNAME = ' 东升 '   --  /

 

    SELECT PHASE . ToString () AS Current_PHASE_AS_Text ,

    ( @ActionUser . GetReparentedValue ( @OldParent , @NewParent ) ). ToString () AS Proposed_PHASE_AS_Text ,

    PHASE AS Current_PHASE ,

    @ActionUser . GetReparentedValue ( @OldParent , @NewParent ) AS Proposed_PHASE ,

    LVL ,

    USERID ,

    USERNAME

    FROM TEST

    WHERE PHASE = @ActionUser ;

 

/*

    Current_PHASE_AS_Text    Proposed_PHASE_AS_Text   Current_PHASE Proposed_PHASE    LVL     USERID      USERNAME

    ----------------------   -----------------------  --------------    ---------------    -------    -----------   ----------

    /1/1.1/                  /1.1/                0x5B16        0x62C0        2      1006       土豆小弟 3

 

    (1 行受影响 )

   

   

    接着上面的代码进行更新

*/

 

    UPDATE TEST

    SET PHASE = @ActionUser . GetReparentedValue ( @OldParent , @NewParent )

    WHERE PHASE = @ActionUser

 

 

    SELECT PHASE . ToString () AS PHASE ,

    LVL , USERID , USERNAME

    FROM TEST

    WHERE USERNAME = ' 土豆小弟 3 ' ; -- /1.1/

 

/*

    PHASE  LVL    USERID      USERNAME

    ------ ------ ---------- ----------

    /1.1/  1      1006        土豆小弟 3

 

    (1 行受影响 )

   

   

 

 

2) 子树的移动

*/

 

    -- 把土豆这棵树移动到红太郎下面

    DECLARE @nold hierarchyid , @nnew hierarchyid

           , @oldP nvarchar ( 256)= ' 土豆 ' , @newP nvarchar ( 256)= ' 红太狼 '

 

 

    SELECT @nold = PHASE FROM TEST WHERE USERNAME = @oldP ;

 

 

           SELECT @nnew = PHASE FROM TEST WHERE USERNAME = @newP ;

          

           SELECT @nnew = @nnew . GetDescendant ( max ( PHASE ), NULL)

           FROM TEST WHERE PHASE . GetAncestor ( 1)= @nnew ;

 

           UPDATE TEST  

           SET PHASE = PHASE . GetReparentedValue ( @nold , @nnew )

           WHERE PHASE . IsDescendantOf ( @nold ) = 1 ;

 

 

 

 

    SELECT PHASE . ToString () AS PHASE , LVL , USERID , USERNAME FROM TEST

    ORDER BY PHASE

   

/* 更新后的结果

    PHASE         LVL    USERID      USERNAME

    ------------  ------ -----------   ----------

    /             0      1001        东升

    /1.1/         1      1006       土豆小弟 3

    /2/           1      1003       红太狼

    /2/1/         2      1002       土豆

    /2/1/1/       3      1004       土豆小弟 1

    /2/1/2/       3      1005       土豆小弟 2

 

    (6 行受影响 )

 

 

8 Read,Write  2 个方法不能使用 Transact-SQL 调用 , 可以使用 CAST CONVERT 代替。

*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值