对于组织架构中的员工层次关系我们应该怎么建模呢?

如下图所示:

此类结构通常有两个主要特点:

1、一个孩子有且只有一个父亲

2、树的深度不确定

为了解决这种结构,我们一般会建一张下面的表:

方案一(Adjacency List)

CREATE TABLE Employees(
employee_id int,

employee_name varchar2(100),

parent_id int

);

每个员工在Employees表中会有一条记录,并通过parent_id来记录其直属领导的employee_id,这样做很简单明了,但是却存在一些弊端。

考虑如下问题:

1、如何得到某个员工的直属领导?

2、如何得到某个领导的直属下属?

3、如何得到某个领导全部下属(下属的下属)?

问题1、2都很简单,一次自连接就解决了:

1、

select par.employee_id,par.employee_name
from employees par,employees self
where self.parent_id=par.employee_id
and self.employee_id=3201

2、

select child.employee_id,child.employee_name
from employees child,employees self
where child.parent_id=self.employee_id
and self.employee_id=1010

但问题3呢?

两种人会有两种做法,一种觉得可以在程序里做,把问题2的SQL循环执行最终把结果拼起来就OK了;

一种是觉得我可以使用多次自连接,比如我知道这下领导最多有两级下属,我就可以这样做:

select child.employee_id,child.employee_name,child1.employee_id,child1.employee_name

from employees self  inner join employees child on child.parent_id=self.employee_id

left join employees child1 on child1.parent_id=child.employee_id and

where self.employee_id=1010

上面两种方法看似都可以解决问题,但是别忘了此类树结构的一个很重要的特点,那就是深度的不确定性(就算确定,如果层次很深,20级),

性能及可扩展性将是一个很大的问题。

那怎么办呢?一时间好像看起来别无他法啊。

好消息是使用Oracle 10g及以上或者SQL Server 2005及以上的朋友可以直接使用数据库特有的SQL特性来解决这个问题了。

例如在Oracle中可以使用层次查询

select EMPLOYEE_ID, employee_name
from employees
start with employee_id = 1
connect by  prior employee_id = parent_id

那使用MySQL或者其不支持层次查询的数据库怎么办呢?难道只能用前面两种笨方法?

答案是否定的,你需要重新设计你的表模型。

How to design?

方案二(Path Enumeration)

CREATE TABLE Employees_Path(
employee_id int,

employee_name varchar2(100),

path varchar2(1000)

);

此种方案借助了unix文件目录的思想,如下图所示:

我们需要做的就是正确的维护这个PATH值,现在如果我们要查询任意领导(比如Michele)的所有下属就只需要这样即可:

select * from Employees_Path where path like '/1/_%'

同样的,如果我们需要查询任意员工(比如Chris)的所有领导也只需要这样即可:

select * from Employees_Path where '/1/2/5/' like path||'%' and path<>'/1/2/5/'

缺点:

1、PATH值由程序来维护,无法在数据库一级确保数据的有效性

2、当树的层级太深有可能会超过PATH字段的长度,所以其能支持的最大深度并非无限的。

方案三(Nested Sets)

CREATE TABLE EMPLOYEES_NESTEDSETS(
EMPLOYEE_ID INT,
EMPLOYEE_NAME VARCHAR2(100),
NSLEFT INT,
NSRIGHT INT
);

该方案采用深度优先遍历给树中的每个节点分配两个值,分别存在NSLEFT和NSRIGHT中。如下图所示

每个节点左边的的值存放在NSLEFT中,右边的值存放在NSRIGHT中;节点左边的值比该节点的所有子孙节点值都要小,节点右边的值比该节点的所有子孙节点值都要大。

例如Hell Mayes左边的值为2,其比Hell Mayes的所有子孙节点的值都要小(3,4,5,10,6,7,8,9)

Hell Mayes右边的值为11,其比Hell Mayes的所有子孙节点的值都要大(3,4,5,10,6,7,8,9)

有了这个规则之后,如果想要查找某个节点的子孙或都祖先就非常容易了。

回到我们前面的题目中来,假设我要查找Helen Mayes的所有下属员工,我们可以这样:

select *
from EMPLOYEES_NESTEDSETS par,
EMPLOYEES_NESTEDSETS child
where child.nsleft > par.nsleft
and child.nsleft < par.nsright
and par.EMPLOYEE_NAME = 'Helen Mayes'

那如果我们要查找Helen Mayes的所有领导呢?

select *
from EMPLOYEES_NESTEDSETS par,
EMPLOYEES_NESTEDSETS child
where child.nsleft > par.nsleft
and child.nsleft < par.nsright
and child.EMPLOYEE_NAME = 'Angela Richards'

Nested Sets这种方案还有一个优点就是,当你删除了一个非叶子节点的时候,该节点的所有子孙节点会自动成为该节点父节点的子孙,并同样满足前面所说的条件。

缺点:

在Adjacency List方案中很好回答的问题,在Nested Sets中却变得困难起来

比如我想要查找任意领导(比如Helen Mayes)的直属下属,在Nested Sets中你需要这样做

select *
from EMPLOYEES_NESTEDSETS par
inner join EMPLOYEES_NESTEDSETS child
on child.nsleft > par.nsleft
and child.nsleft < par.nsright
left join EMPLOYEES_NESTEDSETS tmp
on child.nsleft > tmp.nsleft
and child.nsleft < tmp.nsright
and tmp.nsleft > par.nsleft
and tmp.nsleft < par.nsright
where par.EMPLOYEE_NAME = 'Helen Mayes'
and tmp.employee_id is null

怎么样,够复杂吧? 其逻辑就是 首先找到Helen Mayes的所有下属,然后在去查找这些下属没有属于Helen Mayes下属的上级.........WTF...........

另外,移动和新增加节点也比较复杂

比如我们要在Helen Mayes和Chris Jones之间插入一名员工Scott,如下图所示

从上图基本上就可以看出来我们要更改的地方了......需要重新生成比新插入节点的nsleft值大的所有节点的nsleft和nsright值

非常复杂......

方案四(Closure Table)

CREATE TABLE Employees(
employee_id int,

employee_name varchar2(100)

);

CREATE TABLE TreePaths (
ancestor_key int,

member_key  int,

Distance int,

is_leaf int

);

Closure Table将树中每个节点与其子孙节点的关系都存储了下来,如下图所示:

注意:每个节点都有一条到其本身的记录,如上表的第一条、第四条、第六条记录

Distance是祖先节点到其本身之间的深度

IS_LEAF用于标识该节点是否为叶子节点

有了这张关系表之后,下面让我们用具体例子来感受其带来的好处

查找某个领导(Helen Mayes)的所有下属员工信息

select * from Employees a, TreePaths b where a.employee_id = b.ancestor_key and a.employee_name = 'Helen Mayes' and b.distance<>0

查找某个领导(Helen Mayes)的所有直属员工信息

select * from Employees a, TreePaths b where a.employee_id = b.ancestor_key and a.employee_name = 'Helen Mayes' and b.distance=1

查找某个员工的所有领导

select * from Employees a, TreePaths b where a.employee_id = b.member_key and a.employee_name = 'Helen Mayes' and b.distance<>0

由于时间的关系(现在已经凌晨1点),其它的关于增删改查的方法请大家自行验证。

结语

四种方案中,通常会结合方案一和方案四来使用(BIEE 11g的父子维就使用的该方案);方案三只适合插入和更新极少,查询占主要的应用;方案二不怎么推荐。

树型结构的四种建模方法相关推荐

  1. 树存储结构的几种表示方法

    /* 名称:树存储结构的几种表示方法 说明:对于树的存储结构,一般有以下三种表示方法. (1).双亲表示法.这种存储方式采用一组连续的空间来存储每个结点,同时在每个结点中增设一个伪指针, 指示其双亲在 ...

  2. 清华大学 陈晨 计算机,TNet基于树型结构集群工具软件通信协议.PDF

    第2卷 第6期 444 中国科技论文在线 SCIENCEPAPER ONLINE 2007 年 6 月 TNet :基于树型结构的集群工具软件通信协议 陈 晨,陈文光,郑纬民 ( 清华大学计算机系, ...

  3. mysql如何实现树状结构_实现树状结构的两种方法

    实现树状结构的两种方法 实现树状结构的两种方法 1.递归法 递归是指在函数中显式的调用它自身. 利用递归法实现树状结构的特点是写入数据速度较快,显示速度较慢(在树的分支/层次较多的情况下尤其明显).适 ...

  4. mysql 转成树_Mysql树型结构2种方式及相互转换

    Mysql实现树型结构,数据库上常见有2种方式:领接表.预排序遍历树(MPTT). 领接表方式-- 主要依赖于一个 parent 字段,用于指向上级节点,将相邻的上下级节点连接起来,id 为自动递增自 ...

  5. 无限极分类php简单,创建无限极分类树型结构的简单方法

    先上效果图 顶级分类其实就是一级分类,二级分类也叫作一级分类的子分类,在这个基础上,子分类还可以拥有子分类,这样就构成了无限极分类. 接下来看具体实现的代码: 一.在控制器中按字段查询,查询出所有分类 ...

  6. 结构体的四种表示方法

    转载自https://blog.csdn.net/qq_37271216/article/details/96611887 1. 先定义结构体类型,再定义结构体变量. struct student{ ...

  7. 【唠叨两句】如何将一张树型结构的Excel表格中的数据导入到多张数据库表中...

    小弟昨天遇到一个相对比较棘手的问题,就像标题说的那样.如何将一张树型结构的Excel表格中的数据导入到多张数据库表中,在现实中实际是七张数据库表,这七张表之间有着有着相对比较复杂的主外键关系,对于我这 ...

  8. ipa解包打包工具_ios打包ipa的四种实用方法(.app转.ipa)

    总结一下,目前.app包转为.ipa包的方法有以下几种: 1.Apple推荐的方式,即实用xcode的archive功能 Xcode菜单栏->Product->Archive->三选 ...

  9. maya多边形建模怎样做曲面_一名合格的模型师,不得不学习掌握的几种建模方法,你会了吗?...

    之前说过,想学习三维动画,建议从模型开始学起,因为模型就像高楼大厦的地基,万丈高楼平地起,有了基础,才好学习理解其他的模块. 大厦的建造,每个步骤都是很严谨的,需要按部就班,不然容易出现"烂 ...

最新文章

  1. python中的字典及注意事项
  2. Java中static作用及用法详解
  3. python gui 选择
  4. mysql 写入性能瓶颈_如何通过性能调优突破MySQL数据库性能瓶颈?
  5. RocketMQ——角色与术语详解
  6. [转载红鱼儿]kbmmw 开发点滴:kbmMW在事务中批量执行SQL
  7. async python两个_Python多线程一览
  8. 攻防世界——MyDriver2-397
  9. PHP+txt聊天室
  10. pos机Project v1.1
  11. java基于springboot+vue校园电动自行车管理系统
  12. 企业微信第三方应用开发
  13. java走迷宫课程设计_java课程设计走迷宫.doc
  14. 《程序员的成长课》:增加收入的 3 大方向
  15. matlab实现拉依达准则,拉依达准则matalb程序
  16. python画速度等值线图_python画contour图
  17. 拼多多是PHP还是java,应届程序员收到拼多多offer 表情瞬间爆发
  18. 联想微型计算机重装,联想一体机电脑重装系统教程 一体机电脑重装系统
  19. 使用xib自定义button
  20. 网络爬虫python实例视频-Python网络爬虫实例教程 视频讲解版

热门文章

  1. Visual Studio 2019 设置背景图
  2. 有利于SEO的div+CSS的命名规则小结(技巧)
  3. redis中的increment()方法遇到的问题记录
  4. 硬货专栏 |深入浅出 WebRTC AEC(声学回声消除)
  5. Vue:v-charts图表设置指标别名
  6. Re:从零开始的鸿蒙开发教程
  7. 真正的骨传导耳机有哪些品牌、性价比高的骨传导耳机排名
  8. tkinter-canvas详解
  9. 2023年全国最新二级建造师精选真题及答案59
  10. html图片如何和输入框并排,闪亮的4个小文本输入框并排