以往我们在关系数据库中建立树状结构的时候,通常使用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 值 a 和 b,a 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://www.cnblogs.com/shanyou/archive/2011/07/01/2095968.html

参考文章: http://www.bianceng.cn/database/SQLServer/201408/43555_2.htm

SQL SERVER 2008 Hierarchyid数据类型的更多相关文章

  1. SQL Server 2008空间数据应用系列三:SQL Server 2008空间数据类型

    原文:SQL Server 2008空间数据应用系列三:SQL Server 2008空间数据类型 友情提示,您阅读本篇博文的先决条件如下: 1.本文示例基于Microsoft SQL Server ...

  2. sql server 2008 数据库数据类型

    sql server 2008 数据库数据类型 一.数值型 int:整数类型,它的精度由执行机构确定.. smallint:短整数类型,它的精度由执行机构确定.. numeric(p,s):数值型,并 ...

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

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

  4. SQL Server 2008 geometry 数据类型

    摘自SQL Server 2008帮助 平面空间数据类型 geometry 是作为 SQL Server 中的公共语言进行时 (CLR) 数据类型实现的.此类型表示欧几里得(平面)坐标系中的数据. 注 ...

  5. C#中SQL SERVER 2008字符数据类型使用心得

    一.尽可能使用Varchar,少使用或者不使用Char字符类型 因为char类型输入的数据长度达不到设计长度,会用空格补足,下面是数据表设计图: 下面是编辑前200行的图: 凡是输入的数据长度达不到设 ...

  6. SQL Server 2008中的hierarchyid

    这也是SQL Server 2008的一个重要新增特性.主要解决的问题是拥有层次关系的表格.例如我们日常生活中用到最多的组织结构图.我们一般会用一个Region表保存区域数据,而每个区域则又可能会有相 ...

  7. SQL SERVER 数据类型详解(SQL Server 2008)

    数据类型类别 SQL Server 中的数据类型归纳为下列类别: 数字类型 1.精确数字 2.近似数字 3.日期和时间 字符串类型 4.非Unicode字符串 4.Unicode字符串 5.二进制字符 ...

  8. SQL Server 2008空间数据应用系列五:数据表中使用空间数据类型

    原文:SQL Server 2008空间数据应用系列五:数据表中使用空间数据类型 友情提示,您阅读本篇博文的先决条件如下: 1.本文示例基于Microsoft SQL Server 2008 R2调测 ...

  9. 基于SQL Server 2008 Service Broker构建企业级消息系统

    注:这篇文章是为InfoQ 中文站而写,文章的地址是:http://www.infoq.com/cn/articles/enterprisemessage-sqlserver-servicebroke ...

随机推荐

  1. Android PopupWindow菜单

    初学Android,引用了这篇文章的代码 http://www.cnblogs.com/jiezzy/archive/2012/08/15/2640584.html 使用PopupWindow制作自定 ...

  2. 清除number输入框的上下箭头

    <input type="number"/> 在chrome,firefox,safari浏览器上输入框右侧会有上下箭头 方法1: <input type=&qu ...

  3. 关于C#静态构造函数的几点说明

    静态构造函数是C#的一个新特性,其实好像很少用到.不过当我们想初始化一些静态变量的时候就需要用到它了.这个构造函数是属于类的,而不是属于哪里实例的,就是说这个构造函数只会被执行一次.也就是在创建第一个 ...

  4. js中子页面父页面方法和变量相互调用

    (1)子页面调用父页面的方法或者变量: window.parent.方法()或者变量名window.parent相当于定位到父页面 之后的操作和在父页面中写代码一样写 window.parent.aa ...

  5. Hibernate 系列教程13-继承-鉴别器与内连接相结合

    Employee public class Employee { private Long id; private String name; HourlyEmployee public class H ...

  6. NSIndexPath 延伸

    转载自:http://my.oschina.net/u/2560887/blog/602095?fromerr=Dy4vj5Jd 这个类的实例描述了一个嵌套数组中特定节点的路径,一般叫做索引路径.1. ...

  7. 网络获取的XML的Pull解析

    <?xml version="1.0" encoding="utf-8" ?> - <students> - <student x ...

  8. useradd adduer 的区别

    区别 1). 使用useradd时,如果后面不添加任何参数选项,例如:#sudo useradd test创建出来的用户将是默认“三无”用户:一无Home Directory,二无密码,三无系统She ...

  9. sql 日结

    --生成日结数据 ==================================== -- Author: <Author,,Name> -- Create date: <Cr ...

  10. HDU1102--Constructing Roads(最小生成树)

    Problem Description There are N villages, which are numbered from 1 to N, and you should build some ...