首页 新闻 会员 周边

数据库中如何迅速获取树状结构关系?

0
悬赏园豆:10 [已解决问题] 解决于 2008-11-06 15:29

比如用于描述这样一个人事管理关系:

需要如何设计数据库,并如何进行查询,才能迅速获得某一人的所有下属?

(效率为先,空间换时间的方式也是允许的,但务求速度,尽量优雅)

问题补充: 首先谢谢大家的认真回答:) To 丁学: 我希望做到的应用是没有固定层级上限的,也就是说这会是一个不规则的树状结构。 而且我在获取时并不十分需要了解到下属的确切级别,当然可以获得的话会更好。 我不太清楚你所说的”直系“是指什么,我所要求的就是获取听命于他的所有下属,在日本的概念就是”家臣“。张飞没法对姜维发号施令,但刘备说教张苞的话,张苞是得乖乖听话的。 To eaglet: 你的方法给了我一些启发,但我觉得表1的ParentId可能会是一个冗余点,这和表2的AncestorId冲突了,改进一下应该可以这样: 表1:ID、名称、等级 表2:父级ID、子级ID 这个”等级“就是指每个人所处的层级,其值可以根据其拥有的父级数量来推算得出 这样是在插入新人时进行一次推算,即查找表2中所有其父级作为子级ID的数据,将这些父级都继承一遍,存入数据库,空间换时间。 当然,这还是不太优雅,且维护这样的结构有些吃力,尤其在层级延展到很深的时候,假如我们要进行人事调遣,那将是很头疼的事,期待会有更好的方法出现。
斯克迪亚的主页 斯克迪亚 | 老鸟四级 | 园豆:4124
提问于:2008-10-26 04:41
< >
分享
最佳答案
0

如果你只是想实现快速找到某个人的下属,可以这样实现

建立两个数据表,结构如下

表1 描述实际的树状关系

Id int, --当前记录Id

ParentId int, --上一级Id

Name nvarchar, --姓名

表2 描述相互间关系

Id int, 外键指向表1 的 Id

AncestorId int, --所有上级的Id 

如关兴的记录 在表一中只有一条,即 ParentId 指向 关羽的记录

在表2中有两条记录 AncestorId 指向关羽的 和AncestorId指向刘备的。

查询所有下级只需要将该人的Id找到,并查找表2中 AncestorId =该人Id的记录,就是这个人

的所有下级。

 

 

eaglet | 专家六级 |园豆:17139 | 2008-10-26 08:34
其他回答(4)
0

先来说表结构,如果能够确定一共有多少个级别,那么可以为每一个级别建立一个表,不能确定的话,只能建立一个自引用的表  员工表(员工编号,上司编号,姓名,and so on)

所有下属?直系?还是都算上?直系的话最简单,无论是多表还是一个表,都可以使用 一个 inner join 来获取到

如果是所有下属,多表的情况下比较简单,多个inner join就可以了,惟一要解决的问题是如何知道你要查询的人是哪个级别,因为要用这个来确定where条件和开始inner join的表,所以需要给用户加一标识,而且一直在程序中判断着这个标识

所有下属,单表情况下,能够确定级别数量的话,和上面多表一样的处理方式即可,如果无法确定级别数,问题相当麻烦,因为没办法知道inner join多少次,我能想到的办法一次 inner join 3或5次,通过程序去判断,直到没有新数据被获取(存储过程实现比较好)

直系下属很好,所有下属的话,很是麻烦,尤其是级别数量不确定的时候,上面是最一般的做法,似乎都不太好,我再想想~~~~

丁学 | 园豆:18730 (专家六级) | 2008-10-26 08:43
0

一看到树我就想递归

我从来没考虑过关系型数据库怎么递归

自关联好像每次只能访问到父节点和子节点两层,不能展开到孙子和以下

同问了

JimLiu | 园豆:300 (菜鸟二级) | 2008-10-26 17:54
0

如果可以确定树的层次很少且有限(例如组织结构)这种,可以在编码上做文章。例如:
刘备 是 101
关羽 是 101101
张飞 是 101102
关兴 是 101101101
关索 是 101101102
张苞 是 101102101

那么刘备的所有下属就是 编码 like '101%'
张飞的所有下属就是 编码 like '101102%'

不过这种方发要求实现假定每一层的人数上限。而且如果层次比较多则编码会嗷嗷长。

另一种方法是使用存储过程来实现。先建一个临时表。然后递归每一个层次,把数据插入临时表,然后返回这个临时表。可以使用动态构造SQL语句的方法把它作成通用的存储过程。

1-2-3 | 园豆:200 (初学一级) | 2008-10-27 10:38
0

WITH EmpsCTE AS
(
  SELECT EmployeeID, ReportsTo, FirstName, LastName
  FROM dbo.Employees
  WHERE EmployeeID = 2

  UNION ALL

  SELECT EMP.EmployeeID, EMP.ReportsTo, EMP.FirstName, EMP.LastName
  FROM EmpsCTE AS MGR
    JOIN dbo.Employees AS EMP
      ON EMP.ReportsTo = MGR.EmployeeID
)
SELECT * FROM EmpsCTE;

 

这是我刚从书上看到的。希望对你有帮助,默认为100次递规,如果不限制的话可以SELECT * FROM EmpsCTE OPTION(MAXRECURSION 0)

这里只建立了一个表,另要建立覆盖索引,

CREATE UNIQUE INDEX idx_mgr_emp_ifname_ilname
  ON dbo.Employees(ReportsTo, EmployeeID)
  INCLUDE(FirstName, LastName);

 

你可以根据你的具体情况修改下.

Jerry Qian | 园豆:19 (初学一级) | 2008-10-28 12:55
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册