层次结构数据定义为一组通过层次结构关系互相关联的数据项。 在层次结构关系中,一个数据项是另一个项的父级或子级。

sql server2008开始内置的 hierarchyid 数据类型使存储和查询层次结构数据变得更为容易。hierarchyid 其实是 CLR 数据类型。

废话不多说,看不明白就实际操作一把,然后再回过头来理解。

下面先创建一个表,并插入一些数据:

create table RoleMan
(
NodeId hierarchyid not null,
RoleId int not null,
RoleName nvarchar(32) not null,
Par_NodeId as NodeId.GetLevel() -- GetLevel()用于确定当前层次的深度(级别),最顶层(根节点)为0,然后依次加1。
)
go insert into RoleMan(NodeId,RoleId,RoleName)
select '/1/','','超级管理员' union
select '/1/1/','','管理员A' union
select '/1/2/','','管理员B' union
select '/1/1/1/','','用户AA' union
select '/1/1/2/','','用户AB' union
select '/1/2/1/','','用户BA' union
select '/1/2/2/','','用户BB'
go select *,
NodeId.ToString() NodeId_Path -- 因为 hierarchyid 类型的值是以16进制表示的,这里把他转换为字符串
from RoleMan

查询指定节点的祖先节点:

-- 查询指定节点的祖先节点
declare @NodeId hierarchyid select @NodeId=NodeId
from RoleMan
where RoleId=5 select *,NodeId.ToString() NodeId_Path
from RoleMan
where @NodeId.IsDescendantOf(NodeId)=1 -- IsDescendantOf(NodeId),判断指定节点是否是另一个节点的后代,如果是,则返回1

查询指定节点的子孙节点:

-- 查询指定节点的子孙节点
declare @NodeId hierarchyid select @NodeId=NodeId
from RoleMan
where RoleId=2 select *,NodeId.ToString() NodeId_Path
from RoleMan
where NodeId.IsDescendantOf(@NodeId)=1 -- IsDescendantOf(NodeId),判断指定节点是否是另一个节点的后代,如果是,则返回1

返回指定层级的所有节点:

-- 返回指定层级的所有节点
declare @NodeId hierarchyid select @NodeId=NodeId
from RoleMan
where Par_NodeId=1 -- 指定层级为 1 级 select @NodeId
select *,NodeId.ToString() NodeId_Path
from RoleMan
where NodeId.GetAncestor(0)=@NodeId -- GetAncestor(0),会返回当前层级当前节点的数据 select @NodeId
select *,NodeId.ToString() NodeId_Path
from RoleMan
where NodeId.GetAncestor(1)=@NodeId -- GetAncestor(1),会返回指定层级(@NodeId指定为1级)的下一级的所有节点的数据
-- 数值 1 表示要在层次结构中下降的级别数。

插入新节点:

declare @PNodeId hierarchyid
declare @NodeId hierarchyid select @PNodeId=NodeId
from RoleMan
where RoleId=3 -- 获取 管理员B 的节点,即用于为添加的节点指定父级 select @NodeId=NodeId
from RoleMan
where RoleId=7 -- 获取 用户BB 的节点,即指定添加的节点位于哪个子节点后面 insert into RoleMan(NodeId,RoleId,RoleName)  
values(@PNodeId.GetDescendant(@NodeId, NULL),'','用户BC')  --即在父节点为 '管理员B' 下面的子节点 '用户BB' 后面添加一个节点 '用户BC' select *,
NodeId.ToString() NodeId_Path
from RoleMan

当然,这是父节点下面存在着子节点的时候,那么当父节点下面没有子节点应该怎么添加呢?只需要将 GetDescendant(null,null) 的两个参数都设置为null就行了。如下:

declare @PNodeId hierarchyid

select @PNodeId=NodeId
from RoleMan
where RoleId=8 -- 获取 用户BC 的节点,即用于为添加的节点指定父级 insert into RoleMan(NodeId,RoleId,RoleName)
values(@PNodeId.GetDescendant(null, NULL),'','用户BCA') -- 为无子节点的父节点添加子节点 select *,
NodeId.ToString() NodeId_Path
from RoleMan

如果需要在一个父节点下面的两个子节点之间插入一个子节点,就需要将 GetDescendant(@Child1,@Child2) 的两个参数同时指定。如下:

declare @PNodeId hierarchyid
declare @Child1 hierarchyid
declare @Child2 hierarchyid select @PNodeId=NodeId
from RoleMan
where RoleId=2 -- 获取 管理员A 的节点,即用于为添加的节点指定父级 select @Child1=NodeId
from RoleMan
where RoleId=4 -- 获取第一个子节点 select @Child2=NodeId
from RoleMan
where RoleId=5 -- 获取第二个子节点 insert into RoleMan(NodeId,RoleId,RoleName)
values(@PNodeId.GetDescendant(@Child1, @Child2),'','用户A插队')-- 在父节点 管理员A 的子节点 用户AA 和 用户AB 之间插入一个节点 用户A插队 select *,
NodeId.ToString() NodeId_Path
from RoleMan

变更节点位置:

变更节点位置应当使用 GetReparentedValue 方法,该方法接受两个参数,一个是原节点的 hierarchyid,另一个是目标节点 hierarchyid。

-- 把 管理员B 节点下面的子节点 用户BA 移动到 管理员A 节点的子节点 用户AB 后面
declare @RawNodePath hierarchyid
declare @NewNodePath hierarchyid select @RawNodePath=NodeId
from RoleMan
where RoleId=6 -- 获取节点 用户BA select @NewNodePath=NodeId
from RoleMan
where RoleId=2 -- 获取节点 管理员A select @NewNodePath=@NewNodePath.GetDescendant(MAX(NodeId), NULL) -- 获取节点 管理员A 下面的最大的子节点,即最后一个子节点
from RoleMan
where NodeId.GetAncestor(1)=@NewNodePath -- 获取父节点 管理员A 下面的所有子级 update RoleMan
set NodeId=NodeId.GetReparentedValue(@RawNodePath, @NewNodePath)
where NodeId.IsDescendantOf(@RawNodePath) = 1 select *,
NodeId.ToString() NodeId_Path
from RoleMan go

hierarchyid 函数:

GetLevel():用于确定当前层次的深度(级别),最顶层(根节点)为0,然后依次加1。

ToString():因为 hierarchyid 类型的值是以16进制表示的,ToString()用于将 hierarchyid 类型转换为字符串类型。

IsDescendantOf():判断指定节点是否是另一个节点的后代,如果是,则返回1。一个参数,为指定的节点。

GetAncestor(n):n=0时,会返回当前层级当前节点的数据。否则,会返回指定层级的下 n 级的所有节点。

GetDescendant(null,null) :用于添加节点,该方法接受两个参数,可为空,第一个子节点,第二个子节点。如果第一个参数不为空,表示在指定的父节点下面的子节点后面添加节点,如果两个参数皆为空,则表示要在没有子节点的节点添加节点。

GetReparentedValue():用于变更节点位置,该方法接受两个参数,一个是原节点的hierarchyid,另一个是目标节点hierarchyid。

GetRoot():获取节点的根。

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

Read():Read 从传入的 BinaryReader 读取 SqlHierarchyId 的二进制表示形式,并将 SqlHierarchyId 对象设置为该值。不能使用 Transact-SQL 调用 Read。请改为使用 CAST 或 CONVERT。

Write():Write 将 SqlHierarchyId 的二进制表示形式写出到传入的 BinaryWriter 中。无法通过使用 Transact-SQL 来调用 Write。请改为使用 CAST 或 CONVERT。

hierarchyid 索引策略:

用于对层次结构数据进行索引的策略有两种:深度优先和广度优先。

深度优先索引,子树中各行的存储位置相邻,简而言之,就是以 hierarchyid 值排序的方式存储。

创建深度优先索引:

--创建深度优先索引
create unique index Role_Depth_First
on RoleMan(NodeId) go select *,NodeId.ToString() NodeId_Path
from RoleMan
order by NodeId

广度优先索引,是将层次结构中每个级别的各行存储在一起,简而言之,就是按层级排序的方式存储。

创建广度优先索引:

--创建广度优先索引
create clustered index Role_Breadth_First
on RoleMan(Par_NodeId,NodeId) ;
go select *,NodeId.ToString() NodeId_Path
from RoleMan
order by Par_NodeId,NodeId

参考:

http://blog.csdn.net/zhanghongju/article/details/42584643

https://msdn.microsoft.com/zh-cn/library/bb677173.aspx

SQL Server 使用 Hierarchyid 操作层次结构数据的更多相关文章

  1. Sql Server 函数的操作实例!(执行多条语句,返回Select查询后的临时表)

    Sql Server 函数的操作实例!(执行多条语句,返回Select查询后的临时表) SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO -- ==== ...

  2. Sql Server 函数的操作实例!(返回一条Select语句查询后的临时表)

    Sql Server 函数的操作实例!(返回一条Select语句查询后的临时表) SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE FUN ...

  3. SQL SERVER: 合并相关操作(Union,Except,Intersect)

    SQL SERVER: 合并相关操作(Union,Except,Intersect) use tempdb create table tempTable1 (id int primary key id ...

  4. SQL Server 多库操作 库名.dbo.表名 出错的问题!

    SQL Server 多库操作 库名.dbo.表名 出错的问题! 数据库名不要用数字开头. 例如:343934.dbo.user 这就会出错.md a343934.dbo.user 就没问题!! 记住 ...

  5. SQL SERVER 2008 Hierarchyid数据类型

    以往我们在关系数据库中建立树状结构的时候,通常使用ID+ParentID来实现两条纪录间的父子关系.但这种方式只能标示其相对位置.解决这类问题在SqlServer2005出现之前通常是采用游标来操作, ...

  6. SQL SERVER: 合并相关操作(Union,Except,Intersect) - 转载

    SQL Server 中对于结果集有几个处理,值得讲解一下 1. 并集(union,Union all) 这个很简单,是把两个结果集水平合并起来.例如 SELECT * FROM A UNION SE ...

  7. sql server Geometry 类型操作 笔记

    sqlGeometry 类型为sql server 2008之后的版本 新加的一种CLR扩展数据类型,为广大sql server开发人员存储几何类型及空间运算提供极大的便利,下面说明geometry类 ...

  8. SQL Server数据库远程操作

    SQL Server数据库远程操作中,在使用openrowset/opendatasource前首先要启用Ad Hoc Distributed Queries服务,因为这个服务不安全所以SqlServ ...

  9. SQL Server 跨服务器操作

    Ø  简介 在工作中编写 SQL 时经常会遇到跨库或跨服务器操作,比如查询时,通过 A 服务器的某张表关联 B 服务器某张表,进行连接查询.或者从另一台服务器中的数据,对当前数据库中的数据进行 CRU ...

随机推荐

  1. php -- 设计模式 之 单例模式

    实现单例的条件:三私一公 三私:私有化构造方法:不让外部创建对象 私有化克隆方法:不让外部克隆对象 私有静态属性:保存已经产生的对象 一公:公共静态方法:在类内部创建对象 实例: <?php / ...

  2. Machine Learning With Spark学习笔记(在10万电影数据上训练、使用推荐模型)

    我们如今開始训练模型,还输入參数例如以下: rank:ALS中因子的个数.通常来说越大越好,可是对内存占用率有直接影响,通常rank在10到200之间. iterations:迭代次数,每次迭代都会降 ...

  3. hdu 1086:You can Solve a Geometry Problem too(计算几何,判断两线段相交,水题)

    You can Solve a Geometry Problem too Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/3 ...

  4. MathType中有几种不同的省略号

    省略号是一个使用很广泛的符号,这个符号在很多方面都有应用,它一般表示列举的意思.文科方面的省略号跟数理中的省略号使用时有一些区别,前者是6个点,而后者只要3个点.当在用MathType数学公式编辑器时 ...

  5. C++ 智能指针学习

     C++ Code  12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849 ...

  6. ios开发之--使用AFNetWorking 3.1.0 ,简单的请求封装类

    从苹果系统自带的请求类,到ASIHttpRequest第三方请求类,再到AFNetWorking第三方请求类,目前只要牵扯到数据请求,基本上都是用AFN,所以,这里纯粹是记录下: hRequestTo ...

  7. solr初认识

    Solr : Search On Lucene Replication Solr 基本概况 Apache Solr (读音: SOLer) 是一个开源的搜索服务器.Solr 使用 Java 语言开发, ...

  8. webpack配置(一)

    这里再配置的时候走了些弯路,现在,把配置前的准备工作做好很重要: 首先,安装node.js,当然,npm也就有了: 其次,安装xampp,主要是为了配置Apache: 安装好后,xampp---htd ...

  9. springmvc常用注解标签详解(转载)

    1.@Controller 在SpringMVC 中,控制器Controller 负责处理由DispatcherServlet 分发的请求,它把用户请求的数据经过业务处理层处理之后封装成一个Model ...

  10. Struts2 取消 下载时异常

    Struts2环境下,通过Struts2提供的下载方式进行下载时出现的java.lang.IllegalStateException异常 2011-1-820:34:20 org.apache.cat ...