`
lovnet
  • 浏览: 6817409 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
文章分类
社区版块
存档分类
最新评论

使用SQL Server 2008中的hierarchyid类型来设计具有树型层次关系的表

阅读更多

本文主要讲述三个内容:
1.如何创建hierarychyid的表,插入数据及基本递归查询。
2.介绍hierarchyid的10种专有函数。
3.介绍hierarchyid特有的深度优先索引(Depth-First Indexing)和广度优先索引(Breadth-First Indexing)

在上一节中

http://blog.csdn.net/tjvictor/archive/2009/07/30/4395677.aspx
我们已经演示了如何在SQL Server中通过主键和外键来存储如下图所示的树型结构数据
image

虽然通过主键和外键的相互搭配可以满足我们的查询、存储需求,但是这种方式并不易于管理和维护,幸运的是,在SQL Server 2008中提供了一种新的数据类型hierarchyid和相关的操作方法来存储和查询这种树型层次关系数据。

首先创建数据表:
create database TestDb
go
use TestDb
go
Create table EmployeeTreeTable
(
NodeId hierarchyid PRIMARY KEY,
NodeLevel AS NodeId.GetLevel(),
EmployeeId int UNIQUE NOT NULL,
EmployeeName nvarchar(32) NOT NULL,
)
NodeId是记录树型层次的Id,是hierarchyid类型。NodeLevel是个计算列,用于存储当前树是深度值,根节点为0。关于NodeId.GetLevel()方法将在下面章节中详细介绍。

按照上图所示的层次关系为表插入数据:
--插入数据
declare @DepthNode hierarchyid;--深度Id
declare @BreadthNode hierarchyid;--广度Id
--插入根节点
insert into EmployeeTreeTable values(hierarchyid::GetRoot(),1,'项目经理')
--计算深度并插入子节点2
select @DepthNode = NodeId from EmployeeTreeTable where [EmployeeId] = 1;
insert into EmployeeTreeTable values(@DepthNode.GetDescendant(null,null),2,'技术经理');
--计算节点2广度,在节点2右边插入节点3
select @BreadthNode = NodeId from EmployeeTreeTable where [EmployeeId] = 2;
insert into EmployeeTreeTable values(@DepthNode.GetDescendant(@BreadthNode,null),3,'产品经理');
--计算节点3广度,在节点3右边插入节点4
select @BreadthNode = NodeId from EmployeeTreeTable where [EmployeeId] = 3;
insert into EmployeeTreeTable values(@DepthNode.GetDescendant(@BreadthNode,null),4,'测试经理');
--计算节点2深度并插入子节点5
select @DepthNode = NodeId from EmployeeTreeTable where [EmployeeId] = 2;
insert into EmployeeTreeTable values(@DepthNode.GetDescendant(null,null),5,'技术组长1');
--计算节点5广度,在节点5右边插入节点6
select @BreadthNode = NodeId from EmployeeTreeTable where [EmployeeId] = 5;
insert into EmployeeTreeTable values(@DepthNode.GetDescendant(@BreadthNode,null),6,'技术组长2');
--计算节点4深度并插入子节点7
select @DepthNode = NodeId from EmployeeTreeTable where [EmployeeId] = 4;
insert into EmployeeTreeTable values(@DepthNode.GetDescendant(null,null),7,'测试员工1');
--计算节点5深度并插入子节点8
select @DepthNode = NodeId from EmployeeTreeTable where [EmployeeId] = 5;
insert into EmployeeTreeTable values(@DepthNode.GetDescendant(null,null),8,'技术员工1');
--计算节点8广度,在节点8右边插入节点9
select @BreadthNode = NodeId from EmployeeTreeTable where [EmployeeId] = 8;
insert into EmployeeTreeTable values(@DepthNode.GetDescendant(@BreadthNode,null),9,'技术员工2');
--计算节点9广度,在节点9右边插入节点10
select @BreadthNode = NodeId from EmployeeTreeTable where [EmployeeId] = 9;
insert into EmployeeTreeTable values(@DepthNode.GetDescendant(@BreadthNode,null),10,'技术员工3');
go
select * from EmployeeTreeTable
结果集为:
NodeId NodeLevel EmployeeId EmployeeName
0x 0 1 项目经理
0x58 1 2 技术经理
0x5AC0 2 5 技术组长1
0x5AD6 3 8 技术员工1
0x5ADA 3 9 技术员工2
0x5ADE 3 10 技术员工3
0x5B40 2 6 技术组长2
0x68 1 3 产品经理
0x78 1 4 测试经理
0x7AC0 2 7 测试员工1

1.查询技术组长1所有子节点的员工信息
select * from EmployeeTreeTable
where NodeId.IsDescendantOf(0x5AC0)=1--0x5AC0是技术组长1的NodeId

2.查询技术组长1所有父节点的员工信息
with c as
(
select * from EmployeeTreeTable where EmployeeId = 5
union all
select a.* from EmployeeTreeTable as a
join c on a.NodeId = c.NodeId.GetAncestor(1)
)
select * from c

上面的例子中,使用了很多hierarchyid专有的函数,可能大家还不熟悉,下面我将具体介绍一下hierarchyid的10个函数,分别为:
GetRoot,GetLevel,GetAncestor,GetDescendant,IsDescendantOf,ToString,Parse,GetReparentedValue,Read,Write。
1.GetRoot。返回层次结构树的根节点。注意GetRoot() 是静态方法。
关于SQL中静态方法和实例方法的区别请参见:http://blog.csdn.net/tjvictor/archive/2009/07/29/4390673.aspx
SQL:select * from EmployeeTreeTable where NodeId = hierarchyid::GetRoot()
结果集:
NodeId NodeLevel EmployeeId EmployeeName
0x 0 1 项目经理

2.返回一个表示节点在树中的深度的整数。
前面建表时我们已经使用了这个函数,NodeLevel字段就是用这个函数自动创建的。
SQL:select EmployeeName,NodeId.GetLevel() as TreeLevel from EmployeeTreeTable
结果集为:
EmployeeName TreeLevel
项目经理 0
技术经理 1
技术组长1 2
技术员工1 3
技术员工2 3
技术员工3 3
技术组长2 2
产品经理 1
测试经理 1
测试员工1 2

3.GetAncestor返回表示本节点为的第 n 个父节点的 hierarchyid。
SQL:
declare @NodeId hierarchyid
select @NodeId=NodeId from EmployeeTreeTable where EmployeeId = 5
select EmployeeName,NodeLevel from EmployeeTreeTable where NodeId = @NodeId.GetAncestor(0)
select EmployeeName,NodeLevel from EmployeeTreeTable where NodeId = @NodeId.GetAncestor(1)
select EmployeeName,NodeLevel from EmployeeTreeTable where NodeId = @NodeId.GetAncestor(2)
结果集为:
EmployeeName NodeLevel
技术组长1 2
技术经理 1
项目经理 0
@NodeId.GetAncestor(0) 取自己节点的Id,@NodeId.GetAncestor(1)取父节点的Id,@NodeId.GetAncestor(2)取爷节点的Id,以此类推。

4.GetDescendant返回父级的一个子节点

  • 如果父级为 NULL,则返回 NULL。
  • 如果父级不为 NULL,而 child1 和 child2 为 NULL,则返回父级的子级。
  • 如果父级和 child1 不为 NULL,而 child2 为 NULL,则返回一个大于 child1 的父级的子级。
  • 如果父级和 child2 不为 NULL,而 child1 为 NULL,则返回一个小于 child2 的父级的子级。
  • 如果父级、child1 和 child2 都不为 NULL,则返回一个大于 child1 且小于 child2 的父级的子级。
  • 如果 child1 不为 NULL 且不是父级的子级,则引发异常。
  • 如果 child2 不为 NULL 且不是父级的子级,则引发异常。
  • 如果 child1 >= child2,则引发异常。
    我们在插入的SQL语句中已经使用过了这个方法,这里就不再给出SQL示例,请大家参考前面的插入SQL语句。

    5.IsDescendantOf如果子节点为本节点的后代,则返回 true
    SQL:select * from EmployeeTreeTable where NodeId.IsDescendantOf(0x58)=1
    结果集为:
    NodeId NodeLevel EmployeeId EmployeeName
    0x58 1 2 技术经理
    0x5AC0 2 5 技术组长1
    0x5AD6 3 8 技术员工1
    0x5ADA 3 9 技术员工2
    0x5ADE 3 10 技术员工3
    0x5B40 2 6 技术组长2

    6.ToString返回具有本节点逻辑表示形式的字符串
    SQL:select *,NodeId.ToString() as Path from EmployeeTreeTable
    结果集为:
    NodeId NodeLevel EmployeeId EmployeeName Path
    0x 0 1 项目经理 /
    0x58 1 2 技术经理 /1/
    0x5AC0 2 5 技术组长1 /1/1/
    0x5AD6 3 8 技术员工1 /1/1/1/
    0x5ADA 3 9 技术员工2 /1/1/2/
    0x5ADE 3 10 技术员工3 /1/1/3/
    0x5B40 2 6 技术组长2 /1/2/
    0x68 1 3 产品经理 /2/
    0x78 1 4 测试经理 /3/
    0x7AC0 2 7 测试员工1 /3/1/

    7.Parse将hierarchyid 的规范字符串表示形式转换为hierarchyid值。即与ToString()函数是相反函数。Parse是静态函数。
    SQL:
    declare @Path varchar(32) = '/1/2/5/6/'
    select hierarchyid::Parse(@Path)
    结果集为:0x5B6394

    8.GetReparentedValue把当前节点从旧路径更新到新路径
    下面的SQL是把技术员工3,从技术组长1节点更新到技术组长2下面。
    SQL:
    declare @OldNode hierarchyid=0x5AC0;
    declare @NewNode hierarchyid=0x5B40;
    update EmployeeTreeTable set NodeId = NodeId.GetReparentedValue(@OldNode,@NewNode)
    where EmployeeId = 10
    结果集中技术员工3的路径从/1/1/3/变成了/1/2/3/。
    关于GetReparentedValue的用法比较复杂,我在介绍索引后,会更加详细的说明各种替换情况。

    9.Read和Write
    Read和Write是供CLS调用的,不能在T-SQL中直接使用。所以这里就不具体介绍两个函数的使用方法了。

    hierarchyid有深度优先索引和广度优先索引
    当递归查询父子节点时,会利用到深度优先索引;当平行查询兄弟节点时,会利用到广度优先索引。
    深度优先索引图:
    depth
    广度优先索引图:
    breadth


    1.建立深度优先索引:
    深度优先索引是hierarchyid默认的索引,只要在hierarchyid列上建立主键,那么就会自动建立hierarchyid索引。

    2.建立广度优先索引
    广度优先索引必须是个唯一索引且包括NodeLevel和NodeId两列:
    CREATE UNIQUE INDEX IX_EmployeeBreadth ON Employee(NodeLevel, NodeId)

    需要注意的是采用深度优先、广度优先还是结合使用这两种索引,以及将哪一种设为聚集键(如果有),取决于上述两种查询类型的相对重要性以及 SELECT 与 DML 操作的相对重要性,本文不代表一定要如此建立hierarchyid索引。

    最后我们讨论一下hierarchyid的GetReparentedValue几种使用方法。
    下面我们先看一个有问题的节点更新:把技术组长1从技术经理更新到产品经理。
    SQL:
    declare @OldNode hierarchyid=0x58;
    declare @NewNode hierarchyid=0x68;
    update EmployeeTreeTable set NodeId = NodeId.GetReparentedValue(@OldNode,@NewNode)
    where EmployeeId = 5
    go
    select NodeId.ToString(),* from EmployeeTreeTable
    结果集为:
    路径 NodeId NodeLevel EmployeeId EmployeeName
    / 0x 0 1 项目经理
    /1/ 0x58 1 2 技术经理
    /1/1/1/ 0x5AD6 3 8 技术员工1
    /1/1/2/ 0x5ADA 3 9 技术员工2
    /1/1/3/ 0x5ADE 3 10 技术员工3
    /1/2/ 0x5B40 2 6 技术组长2
    /2/ 0x68 1 3 产品经理
    /2/1/ 0x6AC0 2 5 技术组长1
    /3/ 0x78 1 4 测试经理
    /3/1/ 0x7AC0 2 7 测试员工1
    从结果里面可以看到技术组长已经变成了/2/1,成功更新到产品经理节点下。但是技术组长1下面的子节点技术员工1,2,3却没有相应的更新过来,还是原来的/1/1/1,2,3,但是原先的技术组长1的/1/1节点已经没有了,所以出现了所谓的“断层”现象。
    下面提出几种常用更新需求,并且给出相应的SQL实现语句。

    1.职位变更。例如技术经理与产品经理职位互换。
    针对这种情况,有两种方法。一是把技术经理下面的所有节点Id都更新成产品经理节点下。这种情况变动比较大,不推荐使用。第二种方法是把技术经理的NodeId和产品经理的NodeId互换。下面使用第二种方法:
    declare @TechNode hierarchyid=0x58;
    declare @ProductNode hierarchyid=0x68;
    declare @TempNode hierarchyid=0x59;
    update EmployeeTreeTable set NodeId = @TempNode where NodeId = @TechNode;
    update EmployeeTreeTable set NodeId = @TechNode where NodeId = @ProductNode;
    update EmployeeTreeTable set NodeId = @ProductNode where NodeId = @TempNode;

    2.职位升降级。例如技术组长2降级成为技术员工,被挂在技术组长1节点下:
    declare @TechTeamLeadNode1 hierarchyid=0x5AC0;
    declare @TechEmployeeNode3 hierarchyid=0x5ADE;
    update EmployeeTreeTable set NodeId = @TechTeamLeadNode1.GetDescendant(@TechEmployeeNode3,null)
    where EmployeeId = 6
    部分结果集为:
    Path NodeId NodeLevel EmployeeId EmployeeName
    /1/1/4/ 0x5AE1 3 6 技术组长2
    可见,技术组长2从/1/2变成了/1/1/4

    总结:
    SQL Server 2008提供的hierarchyid类型使我们能够灵活、方便的操作树型结构。关于hierarchyid还有很多深入的知识,很多灵活的用法,本文不可能一一涉及,这里仅是介绍一些基本用法,抛砖引玉,如果大家在以后的使用中发现什么问题或是更好的解决方案,请联系我。

    如需转帐,请注明本文原创自CSDN TJVictor专栏:http://blog.csdn.net/tjvictor/archive/2009/07/30/4395681.aspx

  • 分享到:
    评论

    相关推荐

      sql2008 层次结构id函数 hierarchyid 经典总结

      在SQL Server 2008中,`HierarchyId`是一种特殊的数据类型,用于高效地存储和查询层次结构数据。这种数据类型支持一系列内置方法来处理层次关系,使得在数据库中处理树状结构或层级结构变得更加简单和高效。下面将...

      SQLServer 2008管理专家指南

      《SQL Server 2008管理专家指南》是一本针对SQL Server 2008数据库管理员(DBA)的专业书籍,旨在提供全面且深入的SQL Server管理知识。这本书以其丰富的内容和实用性,被誉为SQL Server的经典之作,是DBA们不可或缺...

      SQL.Server.2008编程入门经典(第3版).pdf

      SQL Server 2008的新增内容和更改包括:DATE和TIME数据类型、 hierarchyID数据类型、MERGE命令和多行插入,以及递归查询等。《SQL Server 2008编程入门经典(第3版)》旨在帮助您快速地掌握Microsoft SQL Selwer2008...

      将sql server 2008的数据库导入到sql server2000上

      2. **评估兼容性**:使用SQL Server Management Studio (SSMS) 的“数据库兼容性级别”检查功能,查看SQL Server 2008数据库中的对象是否可以在SQL Server 2000中运行。某些高级功能可能需要进行调整或替换。 3. **...

      Microsoft SQL Server 2008技术内幕:T-SQL查询(第二卷)

      《Microsoft SQL Server 2008技术内幕:T-SQL查询》全面深入地介绍了Microsoft SQL Server 2008中高级T-SQL查询、性能优化等方面的内容,以及SQL Server 2008新增加的一些特性。主要内容包括SQL的基础理论、查询优化...

      SQL SERVER 2008 新特性 英文版

      表类型和表值参数的引入,使得可以在存储过程和函数中传递整个表作为参数,极大地增强了SQL Server的程序设计能力。 ### MERGE语句和分组集增强 MERGE语句的加入,提供了一种更高效的方式将多个源数据集合并到目标...

      SQL Server 2008简介

      SQL Server 2008是微软公司推出的一款关系型数据库管理系统(RDBMS),它在企业级数据管理和分析中扮演着重要角色。SQL Server 2008在2008年发布,提供了许多增强的功能和改进,旨在提高数据安全性、性能以及数据...

      SQL Server 2008编程入门经典(第3版).

      SQL Server 2008的新增内容和更改包括:DATE和TIME数据类型、hierarchyID数据类型、MERGE命令和多行插入,以及递归查询等。《SQL Server 2008编程入门经典(第3版)》旨在帮助您快速地掌握Microsoft SQL Selwer2008的...

      SQL.Server.2008编程入门经典(第3版).part2.rar

      SQL Server 2008的新增内容和更改包括:DATE和TIME数据类型、hierarchyID数据类型、MERGE命令和多行插入,以及递归查询等。《SQL Server 2008编程入门经典(第3版)》旨在帮助您快速地掌握Microsoft SQL Selwer2008的...

      SQLServer2008技术内幕T-SQL查询包含源代码及附录A

      《Microsoft SQL Server 2008技术内幕:T-SQL查询》全面深入地介绍了Microsoft SQL Server 2008中高级T-SQL查询、性能优化等方面的内容,以及SQL Server 2008新增加的一些特性。主要内容包括SQL的基础理论、查询优化...

      SQL Server 2008 数据库案例教程课后习题答案.docx

      1. SQL Server 2008增加了如HierarchyId数据类型,支持表现树状层次结构的数据。 2. 它提供了报表服务和自定义表数据类型等功能,并支持XML数据操纵语言。 六、关系数据库 1. 关系数据库由表和表间的关联组成,表中...

      SQL.Server.2008编程入门经典(第3版).part1.rar

      SQL Server 2008的新增内容和更改包括:DATE和TIME数据类型、hierarchyID数据类型、MERGE命令和多行插入,以及递归查询等。《SQL Server 2008编程入门经典(第3版)》旨在帮助您快速地掌握Microsoft SQL Selwer2008的...

      sql server 2005 完美转sql server 2000,无错正常运行

      在SQL Server数据库迁移的过程中,从高版本向低版本转换往往会遇到一些挑战,特别是在SQL Server 2005向SQL Server 2000的迁移中。由于不同版本间的功能差异和语法兼容性问题,直接使用默认的方法可能无法顺利完成。...

      SQL Server 2008.ppt

      SQL Server 2008是微软推出的一款关系型数据库管理系统,其代号为"Katmai"。该版本在2008年处于测试阶段,即Beta版,且提供了社区技术预览(CTP)供用户免费下载。CTP版本包含了完整的功能,允许用户从端到端体验。...

      SQL server数据类型

      本文将详细介绍 SQL Server 中常用的数据类型,并通过对比 Microsoft Access 和 MySQL 中的数据类型来加深理解。 #### 二、Microsoft Access 数据类型 Microsoft Access 支持以下几种数据类型: 1. **Text**: ...

      SQLserver数据类型详解[整理].pdf

      SQL Server 数据类型是数据库设计和开发中的核心概念,它们决定了数据如何存储、处理和检索。在SQL Server中,数据类型的选择直接影响着数据的精确性、存储需求和性能。以下是SQL Server中常见的一些数据类型的详细...

      Inside Microsoft SQL Server 2008 T-SQL Querying

      SQL—and unveil the power of set-based querying—with comprehensive reference and advice from a highly regarded T-SQL expert and members of Microsoft's SQL Server development team. Database developers...

    Global site tag (gtag.js) - Google Analytics