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

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

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/‘,‘1‘,‘超级管理员‘ union
select ‘/1/1/‘,‘2‘,‘管理员A‘ union
select ‘/1/2/‘,‘3‘,‘管理员B‘ union
select ‘/1/1/1/‘,‘4‘,‘用户AA‘ union
select ‘/1/1/2/‘,‘5‘,‘用户AB‘ union
select ‘/1/2/1/‘,‘6‘,‘用户BA‘ union
select ‘/1/2/2/‘,‘7‘,‘用户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)[email protected]    -- GetAncestor(0),会返回当前层级当前节点的数据

select @NodeId
select *,NodeId.ToString() NodeId_Path
from RoleMan
where NodeId.GetAncestor(1)[email protected]    -- 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),‘8‘,‘用户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),‘9‘,‘用户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),‘10‘,‘用户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 @[email protected](MAX(NodeId), NULL)    -- 获取节点 管理员A 下面的最大的子节点,即最后一个子节点
from RoleMan
where NodeId.GetAncestor(1)[email protected]    -- 获取父节点 管理员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

原文地址:https://www.cnblogs.com/asdyzh/p/9818650.html

时间: 2024-11-07 23:25:14

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

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

SQL Server 中对于结果集有几个处理,值得讲解一下 1. 并集(union,Union all) 这个很简单,是把两个结果集水平合并起来.例如 SELECT * FROM A UNION SELECT * FROM B [注意]union会删除重复值,也就是说A或B中重复的数据行,最终只会出现一次,而union all则会保留重复行. 2. 差异(Except) 就是两个集中不重复的部分.例如 SELECT * FROM A EXCEPT SELECT * FROM B 这个的意思是,凡

SQL Server数据库远程操作

SQL Server数据库远程操作中,在使用openrowset/opendatasource前首先要启用Ad Hoc Distributed Queries服务,因为这个服务不安全所以SqlServer默认是关闭的. 1.启用Ad Hoc Distributed Queries的方法 SQL Server 阻止了对组件'Ad Hoc Distributed Queries' 的STATEMENT'OpenRowset/OpenDatasource'的访问,因为此组件已作为此服务器安全配置的一部

SQL Server 跨服务器操作

?  简介 在工作中编写 SQL 时经常会遇到跨库或跨服务器操作,比如查询时,通过 A 服务器的某张表关联 B 服务器某张表,进行连接查询.或者从另一台服务器中的数据,对当前数据库中的数据进行 CRUD 操作:又或者对远程服务器的数据进行 CRUD 操作.本文主要讨论在 SQL Server 中如何结合远程服务器中的数据进行操作,以及常用的几种方法. ?  首先,模拟以下场景 1.   A服务器(本地)有一张 Score(成绩)表,数据如下: 2.   B 服务器(远程)有一张 Subject(

SQL SERVER的一些操作

最近忙着折腾一些项目,要用到一些数据库,主要在Oracle ,mysql,sqlserver 和 sqlite 这四类数据库来回折腾,指令常弄混,还是记下来好 ! SQL server 的操作: 在CMD窗口下,直接输入sqlcmd,即可使用控制台操作: 登陆数据库 sqlcmd -S 数据库服务器地址 -U 用户名 -P 密码 或 sqlcmd -S 数据库服务器地址\数据库实例名 -U 用户名 -P 密码 查询数据库 查看有哪些数据库 >1 SELECT name FROM sys.data

sql server Geometry 类型操作 笔记

IF OBJECT_ID ( 'dbo.SpatialTable', 'U' ) IS NOT NULL DROP TABLE dbo.SpatialTable;GO CREATE TABLE SpatialTable ( id int IDENTITY (1,1), GeomCol1 geometry, GeomCol2 AS GeomCol1.STAsText() );GO INSERT INTO SpatialTable (GeomCol1)VALUES (geometry::STGeom

sql server 数据库的操作

Technorati 标记: sql截取,sql修改 把一个字段的部分取出来,插入到新字段 1 update tablename set b = left(a,8) //方法一2 update tablename set b = substring(a,1,8)  //方法二 sql server修改一个字段中的部分数据  update tablename set 字段=replace(字段,'初始内容','替换内容')

SQL SERVER C#数据库操作类

using System;using System.Collections;using System.Collections.Specialized;using System.Data;using System.Data.SqlClient;using System.Configuration; namespace LiTianPing.SQLServerDAL //可以修改成实际项目的命名空间名称{ /**//// <summary> /// Copyright (C) 2004-2008

SQL Server 中同时操作的例子:

在SQL 中同一逻辑阶段的操作是同时发生的. 先有一个例子做为带入: declare @x as int =1;declare @y as int =2;set @[email protected];set @[email protected];select @x,@y;go -- 最后select 的结果是 x=2.y=2!这个结果在大家看来.来的是这么的理直气壮. 那么好我们看一下一个.这次我不把x,y 保存在变量里而是保存到表里. create table t(x int,y int);

追踪SQL Server执行delete操作时候锁的申请与释放过程

一直以为很了解sqlserver的加锁过程,在分析一些特殊情况下的死锁之后,尤其是并发单表操作发生的死锁,对于加解锁的过程,有了一些重新的认识,之前的知识还是有一些盲区在里面的.delete加锁与解锁步骤是怎么样的?什么时候对那些对象,加什么类型的琐,加锁与索引的关系是怎么样的,什么时候释放锁?整个过程锁是如何参与整个delete操作过程表的?这里通过一个非常简单的delete语句,来分析一条delete执行过程中加解锁的过程. 测试表创建 用一个最最简单的例子做了跟踪,对锁的申请和释放,有了更