数据库中,树型结构的存储方式,一般有三种,相邻表,路径关联,闭包表,
这三种方法无非解决两个问题,
1. 结点的信息存储
2. 明确结点之间的关系
同时又会导致查询和更新操作之间的对立,为什么这么说,简单介绍一下,上述三种方法,
相邻表,除了结点信息,只存储父节点的编号,根节点的父节点为0,
路径关联,使用文本方式,记录结点的路径,比如/1/2/5/7/9,表示结点1-2-5-7-9是一条连续的结点路径,
闭包表,以上两种方法,只有一张表,闭包表则使用两张表,一表保存结点信息,一表保存结点间的关系,在关系表中,将会完整记录所有结点间的关系,包括根节点与每个底层结点的关系,这显然存在巨大的数据冗余
--------------------------------------------------------------------------------------
由于结点之间的复杂关系,带来一个问题,如何描述,如果简单描述,比如相邻表和路径关联,
在查询根节点的所有后代结点时,将涉及到循环查询,以及路径文本串的解析,
同时又获得了快速更新的优势,
在闭包表中,利用关系表,只需一次查询,就可得到根节点的所有后代结点,
但是在更新时,由于数据冗余太大,需要同步的关系数据,则相当多,因此更新的开销很大,
------------------------------------------------------------------------------------------------
综合上述方法,得到一个变种,同时满足查询和更新的便利,以及减少存储空间的浪费,
为了结构更清晰,使用两张表,当然也可以选择一张表,
CREATE TABLE TreeNode (
nid int,
name VARCHAR(100),
desc VARCHAR(100)
)
CREATE TABLE TreeRelationship (
nid int,
prior_id int,
next_id int
)
nid - 结点的数字编码
name - 结点的信息存储
desc - 结点的文本描述
prior_id - 当前结点的上代结点
next_id - 当前结点的后代结点
在关系表中,增加上代结点和后代结点,极大减少了闭包中,从根节点到当前节点的路径记录,
同时更新更加便利,比如插入操作,只需修改上代结点的next_id,后代结点的prior_id, 再保存
插入结点的数据即可,
闭包表的优势在于,快速查询某个结点的跨代结点,在变种中,引入desc,即结点的文本描述,
例如一个公司的职位架构,
董事长
总经理
技术部经理/产品部经理/公共部经理/人事部经理
---------------------------------------------------------------
技术部经理
技术部项目A主管/技术部项目B主管/技术部项目C主管
----------------------------------------------------------------
技术部项目A主管
技术部项目A组长
技术部项目A员工/技术部项目A员工/技术部项目A员工/技术部项目A员工/技术部项目A员工/技术部项目A员工/技术部项目A员工/技术部项目A员工
一个公司的职位架构,一般情况下,比较固定,可以基于这些职位名,
进行后代结点的查询,也就是说,利用desc的文本描述,将结点进行分区,
得到一个抽象的区域连接,使得跨代查询更加简单高效,当然与路径关联的方法,
十分相似的,
因此可以查询“技术部”,“项目A”
----------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------
在相邻表的学习中,遇到一个问题,无论是中文页面,还是英文页面,都没有讨论这个问题,
也许是资源不够充分,
A
/ \
B C
在上述相邻表的结点中,需要插入一个D结点,那么问题来了,一个插入函数,
如何判断,插入B或C之前,还是插入A之后,因为相邻表只有父节点的指示,
无法实现后向插入,只能在插入函数中,传入标志位,或者使用两个插入函数。
A
/ \
D \
/ \
B C
A
|
D
/ \
B C