您的位置:首页 > 数据库

SQL SERVER 2008 Hierarchyid数据类型

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

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

SQLSERVER2008引入了新的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<->LauraVijay<->Frank<->James
UseAdventureWorksLT
Go
--SchemeCreation
CreateSchemaHumanResources
Go
--TableCreation
CREATETABLEHumanResources.EmployeeDemo
(
OrgNodeHIERARCHYID,
EmployeeIDINT,
LoginIDVARCHAR(100),
TitleVARCHAR(200),
HireDateDATETIME
)
Go
--IndexCreation
CREATEUNIQUECLUSTEREDINDEXidxEmployeeDemo
ONHumanResources.EmployeeDemo(OrgNode,EmployeeID)

下面插入一些数据
SERTHumanResources.EmployeeDemo(OrgNode,EmployeeID,LoginID,Title,HireDate)
LUES(hierarchyid::GetRoot(),1,'adventure-works\scott','CEO','3/11/05');

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

CLARE@Managerhierarchyid
CLARE@FirstChildhierarchyid
LECT@Manager=hierarchyid::GetRoot()FROMHumanResources.EmployeeDemo;
lect@FirstChild=@Manager.GetDescendant(NULL,NULL)
SERTHumanResources.EmployeeDemo(OrgNode,EmployeeID,LoginID,Title,HireDate)
LUES(@Manager.GetDescendant(@FirstChild,NULL),3,'adventure-works\ravi','DirectorMarketing','4/08/07')

InserttheFirstDescendantofaChildNode
CLARE@Managerhierarchyid
LECT@Manager=CAST('/1/'AShierarchyid)
SERTHumanResources.EmployeeDemo(OrgNode,EmployeeID,LoginID,Title,HireDate)
LUES(@Manager.GetDescendant(NULL,NULL),45,'adventure-works\Ben','ApplicationDeveloper','6/11/07');

InserttheSecondDescendantofaChildNode
CLARE@Managerhierarchyid
CLARE@FirstChildhierarchyid
LECT@Manager=CAST('/1/'AShierarchyid)
LECT@FirstChild=@Manager.GetDescendant(NULL,NULL)

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

InsertthefirstnodewhoistheDescendantofDirectorMarketing
CLARE@Managerhierarchyid
CLARE@FirstChildhierarchyid
LECT@Manager=CAST('/2/'AShierarchyid)

SERTHumanResources.EmployeeDemo(OrgNode,EmployeeID,LoginID,Title,HireDate)
LUES(@Manager.GetDescendant(NULL,NULL),551,'adventure-works\frank','TraineeSalesExec.','12/11/07');

InsertthesecondnodewhoistheDescendantofDirectorMarketing
CLARE@Managerhierarchyid
CLARE@FirstChildhierarchyid
LECT@Manager=CAST('/2/'AShierarchyid)
LECT@FirstChild=@Manager.GetDescendant(NULL,NULL)
SERTHumanResources.EmployeeDemo(OrgNode,EmployeeID,LoginID,Title,HireDate)
LUES(@Manager.GetDescendant(@FirstChild,NULL),531,'adventure-works\vijay','ManagerIndustrialSales','12/09/06');

InsertthethirdnodewhoistheDescendantofDirectorMarketing
inbetween2existingdescendants
CLARE@Managerhierarchyid
CLARE@FirstChildhierarchyid
CLARE@SecondChildhierarchyid
LECT@Manager=CAST('/2/'AShierarchyid)
LECT@FirstChild=@Manager.GetDescendant(NULL,NULL)
LECT@SecondChild=@Manager.GetDescendant(@FirstChild,NULL)
SERTHumanResources.EmployeeDemo(OrgNode,EmployeeID,LoginID,Title,HireDate)
LUES(@Manager.GetDescendant(@FirstChild,@SecondChild),543,'adventure-works\james','ManagerConsumerSales','12/04/06');


Hierarchyid字段类型提供了一系列相关查询函数,可以方便的查询父子关系数据。下面我们查询下数据
DECLARE@TIDhierarchyid
SELECT@TID=OrgNodeFROMHumanResources.EmployeeDemoWHEREtitle='cto'

SELECT*,OrgNode.GetLevel()as层次,OrgNode.ToString()as路径FROMHumanResources.EmployeeDemoWHERE@TID.IsDescendantOf(OrgNode)=1

SELECT*,OrgNode.GetLevel()as层次,OrgNode.ToString()as路径FROMHumanResources.EmployeeDemoWHEREOrgNode.IsDescendantOf(@TID)=1







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

向表里插入记录

SETQUOTED_IDENTIFIERON
GO
--UseSerializableTransaction
CREATEPROCEDURE[dbo].[AddEmployee](@ManagerIDhierarchyid,@EmpIDint,
@LogIDvarchar(100),@JobTitleasvarchar(200),@JoiningDatedatetime)
AS
BEGIN

DECLARE@LastChildhierarchyid
SETTRANSACTIONISOLATIONLEVELSERIALIZABLE
BEGINTRANSACTION
SELECT@LastChild=Max(OrgNode)FromHumanResources.EmployeeDemo
WHEREOrgNode=@ManagerID
INSERTHumanResources.EmployeeDemo(OrgNode,EmployeeID,LoginID,Title,HireDate)
VALUES(@LastChild,@EmpID,@LogID,@JobTitle,@JoiningDate)
COMMIT
END;


移动层级关系


CREATEPROCEDUREMoveOrg(@oldMgrnvarchar(256),@newMgrnvarchar(256))
AS
BEGIN

DECLARE@noldHierarchyID
DECLARE@nnewHierarchyID

SELECT@nold=OrgNodeFROMHumanResources.EmployeeDemoWHERELoginID=@oldMgr;

SETTRANSACTIONISOLATIONLEVELSERIALIZABLE

BEGINTRANSACTION
SELECT@nnew=OrgNodeFROMHumanResources.EmployeeDemoWHERELoginID=@newMgr;
SELECT@nnew=@nnew.GetDescendant(max(OrgNode),NULL)
FROMHumanResources.EmployeeDemoWHEREOrgNode.GetAncestor(1)=@nnew;
UPDATEHumanResources.EmployeeDemo
SETOrgNode=OrgNode.GetReparentedValue(@nold,@nnew)
WHERE@nold.IsDescendantOf(OrgNode)=1

COMMITTRANSACTION
END


获取最大的子节点,传递给GetDescendant()函数获得新的子节点

CreateFunctionGetMyMaxChild(@ManagerIDasBigInt)ReturnsHierarchyID
BEGIN

Declare@ManagerNodeHierarchyID
Declare@MaxChildHierarchyID
--GettheManagerNode
Select@ManagerNode=OrgNodefrom
HumanResources.EmployeeDemoWhereEmployeeID=@ManagerID
--GettheMaxChild

Select@MaxChild=Max(OrgNode)fromHumanResources.EmployeeDemo
WhereOrgNode.GetAncestor(1)=@ManagerNode
--ReturntheValue

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

/article/4674557.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: