首页 > 数据库 >SQL SERVER 2008 Hierarchyid数据类型

SQL SERVER 2008 Hierarchyid数据类型

时间:2022-11-28 14:03:14浏览次数:77  
标签:EmployeeDemo HumanResources hierarchyid 数据类型 Hierarchyid Manager OrgNode 2008 NU

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

以往我们在关系数据库中建立树状结构的时候,通常使用ID+ParentID来实现两条纪录间的父子关系。但这种方式只能标示其相对位置。解决这类问题在SqlServer2005出现之前通常是采用游标来操作,但熟悉数据库内部机制的人都知道使用游标带来的性能问题和其他问题是比较严重的。

到了SqlServer2005下,可以选择用CTE来做递归查询,这种方式查询比较简练,但由于数据库内部是采用递归查询的方式,其效率依旧不高;为了能够实现既简练又高效的查询,通常的做法是增加冗余字段,比如增加一个"Path"字段,查询时用模糊查询来进行左匹配。对Path建索引后,这种查询的效率还是相当高的,因此这种方式也是一种常规的设计方式;

SQL SERVER 2008引入了新的hierarchyid数据类型,可以用它来做本地存储并且在树层次结构中管理其位置.只用这个函数能简洁地表示层次结构中的位置.该函数提供的一些内置的函数方法可以操作和遍历层次结构,使得存储和查询分层数据更为容易,而不需要像那样通过CTE递归来获得.

Hierarchyid类型其实是一个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。

hierarchyid 数据类型的值表示树层次结构中的位置。hierarchyid 的值具有以下属性:

  • 非常紧凑
    在具有 n 个节点的树中,表示一个节点所需的平均位数取决于平均端数(节点的平均子级数)。端数较小时 (0-7),大小约为 6*logAn 位,其中 A 是平均端数。对于平均端数为 6 级、包含 100,000 个人的组织层次结构,一个节点大约占 38 位。存储时,此值向上舍入为 40 位,即 5 字节。
  • 按深度优先顺序进行比较
    给定两个 hierarchyid 值 aba<b 表示在对树进行深度优先遍历时,先找到 a,后找到 b。hierarchyid 数据类型的索引按深度优先顺序排序,在深度优先遍历中相邻的节点的存储位置也相邻。例如,一条记录的子级的存储位置与该记录的存储位置是相邻的。
  • 支持任意插入和删除
    通过使用 ​​GetDescendant​​ 方法,始终可以在任意给定节点的右侧、左侧或任意两个同级节点之间生成同级节点。在层次结构中插入或删除任意数目的节点时,该比较属性保持不变。大多数插入和删除操作都保留了紧凑性属性。但是,对于在两个节点之间执行的插入操作,所产生的 hierarchyid 值的表示形式在紧凑性方面将稍微降低。

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

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

用于对分层数据进行索引的策略有两种:

  • 深度优先
    深度优先索引,子树中各行的存储位置相邻。例如,一位经理管理的所有雇员都存储在其经理的记录附近。
  • 广度优先
    广度优先将层次结构中每个级别的各行存储在一起。例如,同一经理直属的各雇员的记录存储在相邻位置。

例如下面的例子是一个职员表,数据有如下关系:

Scott

|

Mark <- > Ravi

| |

Ben<-> Laura Vijay <-> Frank <-> James

Use AdventureWorksLT 
Go
--Scheme Creation
Create Schema HumanResources
Go
--Table Creation
CREATE TABLE HumanResources.EmployeeDemo
(
OrgNode HIERARCHYID,
EmployeeID INT,
LoginID VARCHAR(100),
Title VARCHAR(200),
HireDate DATETIME
)
Go
--Index Creation
CREATE UNIQUE CLUSTERED INDEX idxEmployeeDemo
ON HumanResources.EmployeeDemo (OrgNode,EmployeeID)

下面插入一些数据

SERT HumanResources.EmployeeDemo (OrgNode, EmployeeID, LoginID, Title, HireDate)  
LUES (hierarchyid::GetRoot(), 1,'adventure-works\scott', 'CEO', '3/11/05') ;

CLARE @Manager hierarchyid
LECT @Manager = hierarchyid::GetRoot() FROM HumanResources.EmployeeDemo;
SERT HumanResources.EmployeeDemo (OrgNode, EmployeeID, LoginID, Title, HireDate)
LUES (@Manager.GetDescendant(NULL,NULL), 2, 'adventure-works\Mark', 'CTO', '4/05/07')



CLARE @Manager hierarchyid
CLARE @FirstChild hierarchyid
LECT @Manager = hierarchyid::GetRoot() FROM HumanResources.EmployeeDemo;
lect @FirstChild = @Manager.GetDescendant(NULL,NULL)
SERT HumanResources.EmployeeDemo (OrgNode, EmployeeID, LoginID, Title, HireDate)
LUES (@Manager.GetDescendant(@FirstChild,NULL), 3, 'adventure-works\ravi', 'Director Marketing', '4/08/07')


Insert the First Descendant of a Child Node
CLARE @Manager hierarchyid
LECT @Manager = CAST('/1/' AS hierarchyid)
SERT HumanResources.EmployeeDemo (OrgNode, EmployeeID, LoginID, Title, HireDate)
LUES (@Manager.GetDescendant(NULL, NULL),45, 'adventure-works\Ben','Application Developer', '6/11/07') ;


Insert the Second Descendant of a Child Node
CLARE @Manager hierarchyid
CLARE @FirstChild hierarchyid
LECT @Manager = CAST('/1/' AS hierarchyid)
LECT @FirstChild = @Manager.GetDescendant(NULL,NULL)

SERT HumanResources.EmployeeDemo (OrgNode, EmployeeID, LoginID, Title, HireDate)
LUES (@Manager.GetDescendant(@FirstChild, NULL),55, 'adventure-works\Laura','Trainee Developer', '6/11/07') ;


Insert the first node who is the Descendant of Director Marketing
CLARE @Manager hierarchyid
CLARE @FirstChild hierarchyid
LECT @Manager = CAST('/2/' AS hierarchyid)

SERT HumanResources.EmployeeDemo (OrgNode, EmployeeID, LoginID, Title, HireDate)
LUES (@Manager.GetDescendant(NULL, NULL),551, 'adventure-works\frank','Trainee Sales Exec.', '12/11/07') ;


Insert the second node who is the Descendant of Director Marketing
CLARE @Manager hierarchyid
CLARE @FirstChild hierarchyid
LECT @Manager = CAST('/2/' AS hierarchyid)
LECT @FirstChild = @Manager.GetDescendant(NULL,NULL)
SERT HumanResources.EmployeeDemo (OrgNode, EmployeeID, LoginID, Title, HireDate)
LUES (@Manager.GetDescendant(@FirstChild, NULL),531, 'adventure-works\vijay','Manager Industrial Sales', '12/09/06') ;


Insert the third node who is the Descendant of Director Marketing
in between 2 existing descendants
CLARE @Manager hierarchyid
CLARE @FirstChild hierarchyid
CLARE @SecondChild hierarchyid
LECT @Manager = CAST('/2/' AS hierarchyid)
LECT @FirstChild = @Manager.GetDescendant(NULL,NULL)
LECT @SecondChild = @Manager.GetDescendant(@FirstChild,NULL)
SERT HumanResources.EmployeeDemo (OrgNode, EmployeeID, LoginID, Title, HireDate)
LUES (@Manager.GetDescendant(@FirstChild, @SecondChild),543, 'adventure-works\james','Manager Consumer Sales', '12/04/06') ;


Hierarchyid字段类型提供了一系列相关查询函数,可以方便的查询父子关系数据。下面我们查询下数据

DECLARE @TID hierarchyid 
SELECT @TID=OrgNode FROM HumanResources.EmployeeDemo WHERE title='cto'

SELECT *, OrgNode.GetLevel() as 层次,OrgNode.ToString() as 路径 FROM HumanResources.EmployeeDemo WHERE @TID.IsDescendantOf(OrgNode)=1

SELECT *, OrgNode.GetLevel() as 层次,OrgNode.ToString() as 路径 FROM HumanResources.EmployeeDemo WHERE OrgNode.IsDescendantOf(@TID)=1

下面另外附几个操作的存储过程:

  • 向表里插入记录
SET QUOTED_IDENTIFIER ON
GO
--Use Serializable Transaction
CREATE PROCEDURE [dbo].[AddEmployee](@ManagerID hierarchyid, @EmpID int,
@LogID varchar(100), @JobTitle as varchar(200), @JoiningDate datetime)
AS
BEGIN

DECLARE @LastChild hierarchyid
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRANSACTION
SELECT @LastChild = Max(OrgNode) From HumanResources.EmployeeDemo
WHERE OrgNode = @ManagerID
INSERT HumanResources.EmployeeDemo (OrgNode, EmployeeID, LoginID, Title, HireDate)
VALUES(@LastChild, @EmpID,@LogID , @JobTitle, @JoiningDate)
COMMIT
END ;
移动层级关系
CREATE PROCEDURE MoveOrg(@oldMgr nvarchar(256), @newMgr nvarchar(256) ) 
AS
BEGIN

DECLARE @nold HierarchyID
DECLARE @nnew HierarchyID

SELECT @nold = OrgNode FROM HumanResources.EmployeeDemo WHERE LoginID = @oldMgr ;

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE

BEGIN TRANSACTION
SELECT @nnew = OrgNode FROM HumanResources.EmployeeDemo WHERE LoginID = @newMgr ;
SELECT @nnew = @nnew.GetDescendant(max(OrgNode), NULL)
FROM HumanResources.EmployeeDemo WHERE OrgNode.GetAncestor(1)=@nnew ;
UPDATE HumanResources.EmployeeDemo
SET OrgNode = OrgNode.GetReparentedValue(@nold, @nnew)
WHERE @nold.IsDescendantOf(OrgNode) = 1

COMMIT TRANSACTION
END


  • 获取最大的子节点,传递给GetDescendant() 函数获得新的子节点
Create Function GetMyMaxChild(@ManagerID as BigInt) Returns HierarchyID 
BEGIN

Declare @ManagerNode HierarchyID
Declare @MaxChild HierarchyID
--Get the ManagerNode
Select @ManagerNode = OrgNode from
HumanResources.EmployeeDemo Where EmployeeID = @ManagerID
--Get the Max Child

Select @MaxChild = Max(OrgNode) from HumanResources.EmployeeDemo
Where OrgNode.GetAncestor(1) = @ManagerNode
--Return the Value

RETURN @MaxChild
END
http://msdn.microsoft.com/zh-cn/library/bb677173.aspx

​http://nibblersrevenge.cluss.de/archive/2009/05/31/how-to-use-hierarchyid-in-linqtosql-or-entity-framework-mssql.aspx​



标签:EmployeeDemo,HumanResources,hierarchyid,数据类型,Hierarchyid,Manager,OrgNode,2008,NU
From: https://blog.51cto.com/shanyou/5891091

相关文章

  • 使用SQL Server Management Studio 2008 将数据库里的数据导成脚本
    之前很羡慕MySQL有这样的工具可以把数据库里的数据导成脚本,SQLServer2005的时候大牛PinalDave写了个​​DatabasePublishingWizard​​​,具体用法参考他写的文章​​......
  • 数据类型及字节
    数据类型强数据语言要求变量的使用要严格符合规定,所有变量都必须先定义后才能使用弱数据语言Java的数据类型分为两大类基本类型(primitivetype)引用类型(reference......
  • python3数据类型
    1. 数字类型 Python数字类型主要包括int(整型)和float(浮点型) int(整型) 在32位机器上,整数的位数是32位,取值范围是-231~231-1,即-2147483648~214748364;在64位系统上,整......
  • PYTHON 数据类型
    1.1数据类型数据都有类型,python的标准类型:数字:int,float,complex,bool字符串:str列表:list元组:tuple集合:set字典:dict可以用type()函数识别数据类型......
  • Redis学习(十)之redis中的数据类型之Streams类型
      1、redis流这种结构类似于日志追加; 2、redis生成一个唯一id为每个流,使用id检索他们的关联条目或者处理后续条目,这个怎么理解???到底是怎么关联的? 3、redis流支持多......
  • vs2008 调试js
    面对一大段的JavaScript脚本,以前总是会很头疼,找不到调试这些代码的方法。如果出现什么错误或异常,总是要从头分析,然后插入很多Alert(),调试起来很麻烦。VisualStudio2008中J......
  • 基本数据类型之间的运算规则
    一、自动类型提升 byte(1)short(2)int(4)long(8)floatdouble当容量小的数据类型的变量与容量大的数据类型的变量做运算时,要用容量大的数据类型来接------结果自......
  • Redis学习(八)之redis中的数据类型之bitmaps类型
      1、集合的每个成员相当于整数的0-N。 2、每个bit就是一个权限,类似文件系统。这两句让我一脸懵。。。。其实就是用bit位来存储0和1,可以用来存储布尔型的数据,一个b......
  • python基础:pycharm下载与使用、python语法之注释、PEP8规范、变量与常量、变量的基本
    目录pycharm下载与使用python语法之注释PEP8规范变量与常量变量的基本使用常量的基本使用数据类型数据类型之整型int数据类型之浮点型float数据类型之字符串str数据类型之......
  • 【Java】数据类型隐式判断缺陷修复
    公司之前有一个与第三方团队合作开发的项目,由于过程沟通出现问题,导致整合测试时出现较多问题。其中有一个最经典的就是关于数据类型隐式判断的缺陷修复,追查代码用了2天的时......