1,传统的分层结构是父子结构,表结构中有一个ParentID字段自引用表的主键,表示“归属”关系,例如

create table dbo.emph
(
ID int not null primary key,
ParentID int foreign key references dbo.emph(id),
Descr varchar(100) not null
)

示例数据是一个简单的HR职称结构,Boss,M表示的Mananger,L表示的是Leader,E表示的是Employee。

2,将父子结构转换为使用HierarchyID的结构

2.1 首先创建一个新表 dbo.emph2,代码以下

create table dbo.emph2
(
idpath hierarchyid not null primary key,
id int not null,
--parentpath as idpath.GetAncestor(1) persisted foreign key references dbo.emph2(idpath),
descr varchar(100)
) create unique nonclustered index idx_emph2_unique on dbo.emph2(id)

代码分析:

  • 不需要ParentID字段,因为HierarchyID类型能够获取Ancestor(1)函数来获取ParentID,所以不需要通过外键关系来限制。
  • IDPath能够标识每一行在分层结构中的位置,但是不能标识每一行的ID,所以ID字段是必须的。
  • 创建一个唯一索引,强制ID唯一。

2.2 将父子结构的数据填充到使用HierarchyID构建的表中

;with cte(idpath,id,descr) as
(
select cast(HierarchyID::GetRoot().ToString() as Varchar(max)) as idpath,ID,Descr
from dbo.emph
where ParentID is null union all select cast(c.idpath+cast(e.id as varchar)+'/' as varchar(max)) as idpath,e.id,e.descr
from dbo.emph e
inner join cte c on e.parentid=c.id
)
insert dbo.emph2 (idpath,id,descr)
select idpath,id,descr
from cte

HierarchyID类型的字符串格式如:‘/1/2/3/’,字符串以‘/’开头,并以‘/’结尾;

HierarchyID类型不会自动生成节点的位置,需要在代码中将父子关系拼接成字符串,

2.3 查询新表dbo.emph2,将HierarchyID类型转换为字符串,能更直观地看出其结构和位置。例如,idpathstring='/2/6/15/'的上一级父节点的path是‘/2/6/’,idpath=‘/2/6/’的ID是6。

select idpath, idpath.ToString() as idpathstring,id,descr
from dbo.emph2

2.4 查看某一个节点的父节点,@NodeID是一个节点ID,@Upcnt是指@Node向上的第几个父节点,如果为Null,那么查询出所有的父节点。

alter procedure dbo.usp_GetAncestor
@NodeID int,
@Upcnt int=null
as
begin declare @node HierarchyID select @node=idpath
from dbo.emph2
where id=@NodeID --get all ancestors
if @Upcnt is null
begin
set @Upcnt=@node.GetLevel() --;with cte(idpath,id,descr,Level) as
--(
-- select idpath,id,descr, 0 as Level
-- from dbo.emph2
-- where id=@NodeID
-- union all -- select e.idpath,e.id,e.descr,c.Level+1 as Level
-- from dbo.emph2 e
-- inner join cte c on e.idpath=c.idpath.GetAncestor(1)
-- where c.Level<@Upcnt
--)
--select idpath,idpath.ToString() as idpathstring,id,descr
--from cte declare @rt table(idpath hierarchyid,idpathstring varchar(max),id int ,descr varchar(100))
declare @i int=0 while @i<=@Upcnt
begin
insert into @rt
select idpath, idpath.ToString() as idpathstring,id,descr
from dbo.emph2
where idpath=@node.GetAncestor(@i); set @i=@i+1
end
select idpath, idpath.ToString() as idpathstring,id,descr
from @rt
end
else
begin
select idpath, idpath.ToString() as idpathstring,id,descr
from dbo.emph2
where idpath=@node.GetAncestor(@Upcnt);
end
end

2.5 查询子节点

alter procedure dbo.usp_GetDescendant
@NodeID int,
@Downcnt int=null
as
begin
declare @Node hierarchyid select @node=idpath
from dbo.emph2
where id=@NodeID select idpath, idpath.ToString() as idpathstring,id,descr
from dbo.emph2
where idpath.IsDescendantOf(@Node)=1
and (@Downcnt is null or (idpath.GetLevel()=@Node.GetLevel()+@Downcnt))
end

2.6 增加一个节点,有时节点的idpath是有顺序的,为了保证顺序,必须使用GetDescendant函数。

--在子节点序列的末尾加入新节点

create procedure dbo.usp_addnode
@parentid int,
@id int,
@descr varchar(100)
as
begin
declare @parentnode hierarchyid
declare @maxchildnode hierarchyid select @parentnode=idpath
from dbo.emph2
where id=@parentid select @maxchildnode=max(idpath)
from dbo.emph2
where idpath.GetAncestor(1)=@parentnode insert into dbo.emph2(idpath,id,descr)
select @parentnode.GetDescendant(@maxchildnode,null),@id,@descr
end
--按照一定的顺序插入子节点

alter procedure dbo.usp_addnode_order
@parentid int,
@childleft int,
@childright int,
@id int,
@descr varchar(100)
as
begin declare @childrightnode hierarchyid
declare @childleftnode hierarchyid
declare @parentnode hierarchyid select @childleftnode=idpath
from dbo.emph2
where id=@childleft select @childrightnode=idpath
from dbo.emph2
where id=@childright select @parentnode=idpath
from dbo.emph2
where id=@parentid insert into dbo.emph2(idpath,id,descr)
select @parentnode.GetDescendant(@childleftnode,@childrightnode),@id,@descr end

对stored procedure 进行测试,并对查询结果进行排序,如下图

exec dbo.usp_addnode 5,25,'L41'
exec dbo.usp_addnode 5,26,'L42'
exec dbo.usp_addnode_order 5,25,26,27,'L43' select idpath, idpath.ToString() as idpathstring,id,descr
from dbo.emph2
order by idpath

2.7 删除一个节点

如果删除的是叶子节点,非常简单,删除叶子节点不会影响其他节点,但是,如果删除的是非叶子节点,必须处理好其子节点,否则,其子节点将会从层次结构游离出来,成为非法存在,所以在删除一个节点的同时,必须为其可能存在的子节点指定一个新的父节点。

alter procedure dbo.usp_deletenode
@deleteid int,
@childnewparentid int
as
begin
declare @deletenode hierarchyid select @deletenode=idpath
from dbo.emph2
where id=@deleteid declare @id int
declare @descr varchar(100) declare cur_child cursor
for select id,descr from dbo.emph2
where idpath.IsDescendantOf(@deletenode)=1 and id!=@deleteid open cur_child fetch next from cur_child into @id,@descr while @@FETCH_STATUS=0
begin
delete dbo.emph2 where id=@id
exec dbo.usp_addnode @childnewparentid,@id,@descr fetch next from cur_child into @id,@descr
end close cur_child
deallocate cur_child delete dbo.emph2 where id=@deleteid
end

注意:IsDescendantOf函数包含当前节点,要想获取当前节点的所有子孙节点,必须将当前节点过滤掉。

测试,将id=5的节点删除,并将其子节点挂在id=4的节点下。

exec dbo.usp_deletenode 5,4

查询结果

select idpath, idpath.ToString() as idpathstring,id,descr
from dbo.emph2
order by idpath

使用存储过程查询id=4的子孙节点

exec dbo.usp_getdescendant 4

2.8 更新一个节点

更新一个节点,变更其父节点,同样面临如何处理其子节点的问题。

create procedure dbo.usp_updatenode
@id int,
@parentid int,
@childnewparentid int
as
begin
--获取节点的idpath
declare @deletenode hierarchyid select @deletenode=idpath
from dbo.emph2
where id=@id --删除旧节点,并变更节点的父节点
declare @descr varchar(100) select @descr=descr
from dbo.emph2
where id=@id delete dbo.emph2 where id=@id exec dbo.usp_addnode @parentid,@id,@descr --逐个变更子节点的父节点
declare @childid int
declare @childdescr varchar(100) declare cur_child cursor
for select id,descr from dbo.emph2
where idpath.IsDescendantOf(@deletenode)=1 and id!=@id open cur_child
fetch next from cur_child into @childid,@childdescr while @@FETCH_STATUS=0
begin
delete dbo.emph2 where id=@childid
exec dbo.usp_addnode @childnewparentid,@childid,@childdescr fetch next from cur_child into @childid,@childdescr
end close cur_child
deallocate cur_child
end

测试数据如下

测试的目的是将id=5的所有子节点挂在id=4的节点下,并强id=5的父节点变更为id=8的节点

exec dbo.usp_updatenode 5,8,4

查询结果

select idpath, idpath.ToString() as idpathstring,id,descr
from dbo.emph2
order by idpath

Design3:使用HierarchyID构建数据的分层结构的更多相关文章

  1. Design2:使用HierarchyID构建数据的分层结构

    1,传统的分层结构是父子结构,表结构中有一个ParentID字段自引用表的主键,表示“归属”关系,例如 create table dbo.emph ( ID int not null primary ...

  2. 基于腾讯云存储COS的ClickHouse数据冷热分层方案

    一.ClickHouse简介 ClickHouse是一个用于联机分析(OLAP)的列式数据库管理系统(DBMS),支持PB级数据量的交互式分析,ClickHouse最初是为YandexMetrica ...

  3. 基于Apache Hudi构建数据湖的典型应用场景介绍

    1. 传统数据湖存在的问题与挑战 传统数据湖解决方案中,常用Hive来构建T+1级别的数据仓库,通过HDFS存储实现海量数据的存储与水平扩容,通过Hive实现元数据的管理以及数据操作的SQL化.虽然能 ...

  4. 内核中用于数据接收的结构体struct msghdr(转)

    内核中用于数据接收的结构体struct msghdr(转) 我们从一个实际的数据包发送的例子入手,来看看其发送的具体流程,以及过程中涉及到的相关数据结构.在我们的虚拟机上发送icmp回显请求包,pin ...

  5. H264的句法和语法总结(一)分层结构

    在H.264 中,句法元素共被组织成  序列.图像.片.宏块.子宏块五个层次.在这样的结构中,每一层的头部和它的数据部分形成管理与被管理的强依赖关系,头部的句法元素是该层数据的核心,而一旦头部丢失,数 ...

  6. 【驱动】网卡驱动·linux内核网络分层结构

    Preface   Linux内核对网络驱动程序使用统一的接口,并且对于网络设备采用面向对象的思想设计. Linux内核采用分层结构处理网络数据包.分层结构与网络协议的结构匹配,既能简化数据包处理流程 ...

  7. [转]linux内核网络分层结构

    Preface   Linux内核对网络驱动程序使用统一的接口,并且对于网络设备采用面向对象的思想设计. Linux内核采用分层结构处理网络数据包.分层结构与网络协议的结构匹配,既能简化数据包处理流程 ...

  8. Linux 网络设备驱动开发(一) —— linux内核网络分层结构

    Preface Linux内核对网络驱动程序使用统一的接口,并且对于网络设备采用面向对象的思想设计. Linux内核采用分层结构处理网络数据包.分层结构与网络协议的结构匹配,既能简化数据包处理流程,又 ...

  9. JS函数动作分层结构详解及Document.getElementById 释义 js及cs数据类型区别 事件 函数 变量 script标签 var function

    html +css 静态页面 js     动态 交互   原理: js就是修改样式, 比如弹出一个对话框. 弹出的过程就是这个框由disable 变成display:enable. 又或者当鼠标指向 ...

随机推荐

  1. 高德地图API编译警告

    版本: V2.1.1 警告内容: (null): warning: (i386) /Users/xiaominghan/Desktop/autonavi/MAMapKit_3D_r923_201310 ...

  2. PHP, LDAPS and Apache

    要PHP可以连接到用self-signed certificate的ldaps服务器,需要在/etc/ldap.conf中添加一行: TLS_REQCERT     never 要PHP在Apache ...

  3. MFC程序中使用调试宏ASSERT()、ASSERT_VALID()、VERIFY()和TRACE()的区别

    其实这篇文章说的很明白了:http://dev.gameres.com/Program/Other/DebugMacro.htm 结论如下: 1.ASSERT()测试它的参数,若参数为0,则中断执行并 ...

  4. 20145301&20145321&20145335实验三

    20145301&20145321&20145335实验三 这次实验我的组员为:20145301赵嘉鑫.20145321曾子誉.20145335郝昊 实验内容详见:实验三

  5. java的 new 关键字

    java的new关键字想必大家都知道这是实例化一个对象.没错,也是为新对象分配内存空间. 比如new MyDate(22,7,1964)这样一个案例,他的完成需要四部: 一.为新对象分配内存空间,将M ...

  6. 如何在Hdevelop加入自己的算子

    halcon中允许用户编写自定义函数,同时也可以将此函数保存在其他工程中调用.   以halcon12讲解   创建自定义函数     本地程序函数:创建后仅能在当前工程使用 hdevelop函数文件 ...

  7. 直接通过Ajax 处理程序加 Action名,取得变量值。

    var set_value;$(document).ready(function () {            var query = createParam('GetValue', '0');   ...

  8. 打开mysql时,提示 1040,Too many connections

    打开mysql时,提示 1040,Too many connections,这样就无法打开数据库,看不了表里边的内容了. 出现这个问题的原因是,同时对数据库的连接数过大,mysql默认的最大连接数是1 ...

  9. 来科普下游标(MSSQL)这东西。。。

    刚刚接到一个面试电话,对头的先生问我懂不懂触发器和存储过程,当时是觉得有些好笑,毕竟“视图.触发和存储”是咱数据库工程师的吉祥三宝,怎么可能不认识?只是稍后他还问了下游标这东西,仔细想想我是不常使用C ...

  10. 压力测试工具ab使用

    ab全名是apache bench,是apache自带的一款压力测试工具.它通过创建多个线程来模拟并发,测试目标是基于URL的,因此不论是什么web服务器都可以支持. 使用ab非常简单,进入apach ...