树形层次结构(Hierarchy)经常出现在有结构的数据中,T-SQL新增数据类型HierarchyID, 其长度可变,用于存储层次结构中的路径。HierarchyID表示的层次结构是树形的,由应用程序来生成和分配 HierarchyID的值,建立父子节点之间的关系。

HierarchyID数据类型支持深度优先顺序的比较,对于两个HierarchyID值 a和b,a<b意味着,在深度优先遍历时,先遍历到a,后遍历到b,也就是说,值越小,越接近根节点。对Hierarchy数据类型创建索引,是按照深度优先,先左后右的顺序来排序的。左和右是根据节点的值来判断的,在同一深度上,值较小的节点在父节点的左边。

一,类型的赋值

HierarchyID数据类型存储的是单个节点在树形结构中的路径(Path),路径从根节点(Root Node)开始,根节点是“/”,路径以“/”结尾,使用整数表示一个节点。这意味着HierarchyID的值必须以“/”开头,以“/”结尾,“/”之间使用数值(正整数或正小数)标识一个元素,例如:“/”,“/1/2/”,“/1/2/3/”,"/1/2.1/3"。

有3种赋值方式,通过字符串赋值,字符串转换和通过整数赋值。

declare @ha HierarchyID
declare @hb HierarchyID
declare @hc HierarchyID set @ha='/1/2/3/'
set @hb=HierarchyID::Parse('/1/2/3/')
set @hc=0x5B5E select @ha as ha,@hb.ToString() as hb,@hc.ToString() as hc

二,按深度优先顺序进行比较 

给定两个 hierarchyid 值 a 和 b,a<b 表示在对树进行深度优先遍历时,先找到 a,后找到 b。hierarchyid  数据类型的索引按深度优先顺序排序,在深度优先遍历中相邻的节点的存储位置也相邻。同级别的节点,左边节点小于右边节点,表示左边先被遍历到。

declare @ha HierarchyID
declare @hb HierarchyID
declare @hc HierarchyID set @ha=HierarchyID::Parse('/1/2/')
set @hb=HierarchyID::Parse('/1/2/3/')
set @hc=HierarchyID::Parse('/1/2/4/') select iif(@ha>=@hb,'>=','<'),iif(@hb>=@hc,'>=','<')

三,用于HierarchyID数据类型的函数

1,获取当前值的级数(Level)

调用GetLevel()查看HierarchyID的Level,值是从root节点开始的层数

declare @ha HierarchyID
set @ha=HierarchyID::Parse('/1/2/3/') select @ha.GetLevel() as Level

2,获取根节点

静态方法GetRoot(),静态方法的调用格式:HierarchyID::GetRoot()

select HierarchyID::GetRoot().ToString() as TootString,HierarchyID::GetRoot() as RootHierarchyID

3,返回子节点

GetDescendant(childleft,childright)用以返回父级的一个子节点,返回的子节点和child是同level的。

declare @sa Nvarchar(100)
declare @sb Nvarchar(100)
declare @sr Nvarchar(100)
declare @ha HierarchyID
declare @hb HierarchyID
declare @hr HierarchyID set @sa='/1/2/3/'
set @sb='/1/2/6/'
set @sr='/1/2/'
set @ha=HierarchyID::Parse(@sa)
set @hb=HierarchyID::Parse(@sb)
set @hr=HierarchyID::Parse(@sr) select @hr.GetDescendant(null,null).ToString(),
@hr.GetDescendant(@ha,null).ToString(),
@hr.GetDescendant(@ha,@hb).ToString()

如果LeftChild是‘/1/2/3’,RightChild是‘/1/2/4’,需要在这两个节点之间插入一个新的节点,需要如何处理?表示节点的数字,并不一定必须是正整数,小数也可以,如下,NewChild=’/1/2/3.1/‘;

declare @sa Nvarchar(100)
declare @sb Nvarchar(100)
declare @sr Nvarchar(100)
declare @ha HierarchyID
declare @hb HierarchyID
declare @hr HierarchyID set @sa='/1/2/3/'
set @sb='/1/2/4/'
set @sr='/1/2/'
set @ha=HierarchyID::Parse(@sa)
set @hb=HierarchyID::Parse(@sb)
set @hr=HierarchyID::Parse(@sr) select @hr.GetDescendant(null,null).ToString(),
@hr.GetDescendant(@ha,null).ToString(),
@hr.GetDescendant(@ha,@hb).ToString()

4,判断两个节点之间的父子关系

判断是否是节点的后代,child.IsDescendantOf(parent),如果是,返回1,如果不是,返回0

declare @sa Nvarchar(100)
declare @sb Nvarchar(100)
declare @sr Nvarchar(100)
declare @ha HierarchyID
declare @hb HierarchyID
declare @hr HierarchyID set @sa='/1/2/3/'
set @sb='/1/2/6/'
set @sr='/1/2/'
set @ha=HierarchyID::Parse(@sa)
set @hb=HierarchyID::Parse(@sb)
set @hr=HierarchyID::Parse(@sr) select @ha.IsDescendantOf(@hr),
@hb.IsDescendantOf(@hr),
@ha.IsDescendantOf(@hb)

四,HierarchyID的值的更新

更新HierarchyID的值,必须级联地更新与该节点相关的子节点的值,这是由于HierarchyID类型自身的局限性导致的。

HierarchyID数据类型具有以下局限性:

  • 类型为 HierarchyID的列不会自动表示树。由应用程序来生成和分配 hierarchyid 值,使行与行之间的所需关系反映在这些值中。 某些应用程序可能具有 hierarchyid 类型的列,该列指示在另一个表中定义的层次结构中的位置。
  • 由应用程序来管理生成和分配 hierarchyid 值时的并发情况。不能保证列中的 hierarchyid 值是唯一的,除非应用程序使用唯一键约束或应用程序自身通过自己的逻辑来强制实现唯一性。
  • 由 hierarchyid 值表示的层次结构关系不是像外键关系那样强制实现的。  可能会出现下面这种层次结构关系而且有时这种关系是合理的:A 具有子级 B,然后删除了 A,导致 B 与一条不存在的记录之间存在关系。 如果这种行为不可接受,应用程序在删除父级之前必须先查询其是否有后代

1,创建数据源

create table dbo.emph2
(
idpath hierarchyid not null primary key,
id int not null,
parentid as idpath.GetAncestor(1) persisted foreign key references dbo.emph2(idpath),
descr varchar(100)
)

idpath=’/1/2/6/‘的子孙节点如下图

select e.idpath.ToString() as IDPath,e.id,e.parentid.ToString() as ParentIDPath,e.descr
from dbo.emph2 e
where e.idpath.IsDescendantOf(HierarchyID::Parse('/1/2/6/'))=1

2,把子节点变成另一个节点的父节点

例如,把idpath=’/1/2/6/‘ 的节点删除,并将其子节点的父节点变更为idpath=’/1/2/7/‘

由于存在外键关系,必须先变更子节点的父节点,然后再删除idpath=’/1/2/6/‘ 的节点。

--delete child notes
--select e.idpath.ToString() as IDPath,e.id,e.parentid.ToString() as ParentIDPath,e.descr
update e set e.idpath=HierarchyID::Parse('/1/2/7/'+cast(e.id as varchar)+'/')
from dbo.emph2 e
where e.idpath.IsDescendantOf(HierarchyID::Parse('/1/2/6/'))=1 and e.idpath!=HierarchyID::Parse('/1/2/6/') --delete parent note
delete dbo.emph2 where idpath=HierarchyID::Parse('/1/2/6/') --check
select e.idpath.ToString() as IDPath,e.id,e.parentid.ToString() as ParentIDPath,e.descr
from dbo.emph2 e
where e.idpath.IsDescendantOf(HierarchyID::Parse('/1/2/7/'))=1

3,变更父节点

例如,把idpath=’/1/2/6/‘的节点的父节点变更,其子节点仍然是其子节点。

思路是新建一个节点,并将子节点都挂在新节点下。

--create new node
insert into dbo.emph2(idpath,id,descr)
select HierarchyID::Parse('/1/3/6/'),id,descr
from dbo.emph2 e
where e.idpath=HierarchyID::Parse('/1/2/6/') --delete child notes
--select e.idpath.ToString() as IDPath,e.id,e.parentid.ToString() as ParentIDPath,e.descr
update e set e.idpath=HierarchyID::Parse('/1/3/6/'+cast(e.id as varchar)+'/')
from dbo.emph2 e
where e.idpath.IsDescendantOf(HierarchyID::Parse('/1/2/6/'))=1 and e.idpath!=HierarchyID::Parse('/1/2/6/') --delete parent note
delete dbo.emph2 where idpath=HierarchyID::Parse('/1/2/6/') --check
select e.idpath.ToString() as IDPath,e.id,e.parentid.ToString() as ParentIDPath,e.descr
from dbo.emph2 e
where e.idpath.IsDescendantOf(HierarchyID::Parse('/1/3/6/'))=1

4,定向插入新的节点

由于节点之间存在先后顺序,使用GetDescendant(ChildLeft,ChildRight)保证顺序。

在节点 idpath=’/1/2/6/‘ 的子节点 id=15,id=16之间插入一个新的子节点,新的子节点的id=36,descr=‘E1136’,思路是使用GetDescendant(ChildLeft,ChildRight)获取新的IDPath,然后插入到表中。

declare @id int
declare @descr Nvarchar(100)
declare @sa Nvarchar(100)
declare @sb Nvarchar(100)
declare @sr Nvarchar(100)
declare @hnew HierarchyID set @id=36
set @descr='E1136'
set @sa='/1/2/6/15/'
set @sb='/1/2/6/16/'
set @sr='/1/2/6/' set @hnew= HierarchyID::Parse(@sr).GetDescendant(HierarchyID::Parse(@sa),HierarchyID::Parse(@sb)) insert into dbo.emph2(idpath,id,descr)
values(@hnew,@id,@descr)
select e.idpath.ToString() as IDPath,e.id,e.parentid.ToString() as ParentIDPath,e.descr
from dbo.emph2 e
where e.idpath.IsDescendantOf(HierarchyID::Parse('/1/2/6/'))=1
order by e.idpath

从排序的结果集中可以看出,id=36的节点,处于id=15和id=16的节点之间,通过GetDescendant(ChildLeft,ChildRight)实现了顺序。

五, 遍历

HierarchyID类型的数据,很容易实现广度优先遍历和深度优先遍历

1,广度优先遍历是指查询层次结构中相同级别的节点

select idpath.ToString() as IDPath,id,parentid.ToString() as ParentIDPath,descr
from dbo.emph2 where idpath.GetLevel()=2

2,深度优先遍历是指遍历一个节点的所有子节点

select idpath.ToString() as IDPath,id,parentid.ToString() as ParentIDPath,descr
from dbo.emph2
where idpath.IsDescendantOf(HierarchyID::Parse('/1/2/6/'))=1

参考文档:

hierarchyid data type method reference

HierarchyID 数据类型用法的更多相关文章

  1. TSql HierarchyID 数据类型用法

    HierarchyID 数据类型是一种长度可变的系统数据类型.可使用 HierarchyID 表示层次结构中的位置.类型为 HierarchyID 的列不会自动表示树,由应用程序来生成和分配 Hier ...

  2. SQL SERVER 2008 Hierarchyid数据类型

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

  3. SQL Server2008 Hierarchyid数据类型

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

  4. JAVA自定义数据类型用法

    一,自定义数据类型的概念:    我们就拿一部手机进行分析,它能用来做什么呢?它可以打电话,上网,聊微信等,这些就是 手机所提供的功能,也就是方法:手机也有它的特征,如颜色.尺寸大小.品牌型号等,这些 ...

  5. SQL Server 使用 Hierarchyid 操作层次结构数据

    层次结构数据定义为一组通过层次结构关系互相关联的数据项. 在层次结构关系中,一个数据项是另一个项的父级或子级. sql server2008开始内置的 hierarchyid 数据类型使存储和查询层次 ...

  6. HierarchyId通过父节点创建一个新的子节点

    --HierarchyId通过父节点创建一个新的子节点 CREATE TABLE #temp( node HierarchyID ); insert into #temp select '/' uni ...

  7. 【转】SQL Server 2008 新数据类型

    概览: 新日期和时间数据类型 代表在层次结构中的位置 用于处理空间数据的两种模型 在全球经济环境下开展业务这一趋势越来越要求各公司使用新型的数据.应用程序以及复杂的计算.SQL Server 2008 ...

  8. 层次关系表格,不用递归,快速检索。HierarchyId

    最近这几天写了个T4自动实现EF code first和Ado的存储过程.使用过程中发现了一个Sql的类型为HierarchyId.看到时真是百思不得齐姐.算了查一下MSDN吧.从微软官网找到了Hie ...

  9. 如何在SqlServer中使用层级节点类型hierarchyid

    Sql Server2008开始新增的 hierarchyid 数据类型使存储和查询层次结构数据变得更为简单. 为了使用这个类型,笔者在此进行简单记录,同时为需要的朋友提供一个简单的参考. --获取层 ...

随机推荐

  1. [js高手之路]html5 canvas动画教程 - 重力、摩擦力、加速、抛物线运动

    上节,我们讲了匀速运动,本节分享的运动就更有意思了: 加速运动 重力加速度 抛物线运动 摩擦力 加速运动: <head> <meta charset='utf-8' /> &l ...

  2. Md2All

    微信公众号:颜家大少欢迎关注我,一起学习,一起进步!目前,知到 Md2All 的朋友还很少,如果你觉得有帮助,希望能告诉身边有需要的朋友. 谢谢! Md2All 简介 一个Markdown在线转换工具 ...

  3. MAC上安装mysql及workbench

    下载mysql for mac    https://dev.mysql.com/downloads/installer/ 官网下载很慢---百度云:链接: https://pan.baidu.com ...

  4. 【转载】MySQL事务以及SELECT ... FOR UPDATE的使用

    MySQL中的事务,默认是自动提交的,即autocommit = 1: 但是这样的话,在某些情形中就会出现问题:比如: 如果你想一次性插入了1000条数据,mysql会commit1000次的, 如果 ...

  5. IOS 看懂此文,你的block再也不需要WeakSelf弱引用了!

    前言: 最近都在折腾 Sagit 架框的内存释放的问题,所以对这一块有些心得. 对于新手,学到的文章都在教你用:typeof(self) __weak weakSelf = self. 对于老手,可能 ...

  6. 【数论】洛谷P1313计算系数

    题目描述 给定一个多项式(by+ax)^k,请求出多项式展开后x^n*y^m 项的系数. 输入输出格式 输入格式: 输入文件名为factor.in. 共一行,包含5 个整数,分别为 a ,b ,k , ...

  7. 易卡易APP的出现改变你的消费习惯

    科技发展越来越快,移动支付占据市场主导地位,银行业发展受到了重大冲击,致使银行对于信用卡的推广更加重视.人们的消费观念也受到了很大影响从以前的现金消费变成现在的数字消费,人们对于金钱的观念就是一个数字 ...

  8. 五分钟学习React(一): 什么是React

    在前端的世界里,我们要处理的文件不是太多,而是太少.每天开发项目将html.css.js.图片.字体文件都像大杂烩一般加载都网页上.当应用变得越来越臃肿的时候,会发现js用了那么多全局变量,css的继 ...

  9. 论python3下“多态”与“继承”中坑

    1.背景: 近日切换到python3后,发现python3在多态处理上,有一些比较有意思的情况,特别记载,供大家参考... 以廖老师的python3教程中的animal 和dog的继承一节的代码做例子 ...

  10. java_web学习(九) PreparedStatement动态参数的引入

    一.PreparedStatement 概述 在数据库的操作过程中,PreparedStatement 对象是一个很不起眼但是记为重要的接口对象,它继承 于Statement,并与之在两方面有所不同: ...