tree闭包表

目标


在一颗树中快速查找到子孙节点、祖先节点

查找节点A的所有子孙节点


图1

树的表结构

CREATE TABLE `node` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT '' COMMENT '节点名称',
  `parent_id` bigint(20) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4

node表的数据

id name parent_id
1 A 0
2 B 1
3 C 1
4 D 2
5 E 2
6 F 3

查找节点A的子孙节点,可以用以下3个SQL完成

select … where parent_id = A ==> B, C
select … where parent_id in (B, C) ==> D,E,F
select … where parent_id in (D,E,F) ==> empty

可以看到随着节点深度的增加,获取子孙节点SQL条数会越来越多。
如何进行优化,来避免和SQL的多次交互?闭包表一次就能获取节点的子孙部门或者祖先部门

什么是闭包表

一张记录树中所有节点的关系表。记录节点之间距离的关系表,注意:这次讨论的闭包关系表不包含自身的关系,例如 (ancestor,descendant,distance) => (A,A,0)
表结构如下:

CREATE TABLE `node_relation` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID',
  `ancestor` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '祖先节点',
  `descendant` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '后代节点',
  `distance` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '相隔层级,>=1',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uniq_anc_desc` (`ancestor`,`descendant`),
  KEY `idx_desc` (`descendant`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8mb4 COMMENT='节点关系表'
图2

node_relation表A作为祖先节点的的关系数据(id 用实际节点描述表示)

ancestor descendant distance
A B 1
A C 1
A D 2
A E 2
A F 2

再来看查A部门的所有子部门怎么查,只需要一个SQL就搞定了

select descendant from ancestor = A;

闭包表副作用

由于闭包表新增了节点和节点之间的关系,所以在变更树结构的时候,会重构这个关系,想想就觉得复杂。所以数据量少,请谨用闭包表。

如果实在是需要用到闭包表的,那么请继续往下看,本文会为你理清这些关系。

闭包表常见操作

闭包表-查询

1).获取指定A节点的子孙节点

   select descendant from node_relation where  ancestor = A;

2).获取指定节点F的祖先节点

   select ancestor from node_relation where descendant = F;

闭包表-增、删、move

1).新增:在F节点下新增节点H(见图3)


图3
//新增节点H
insert into node (`name`,`parent_id`)  values ("H",F_id);
//记录F、H的闭包关系
insert into node_relation (ancestor,descendant,distance) values(F,H,1); 
;//获取F和祖先闭包关系
select ancestor,descendant,distance from node_relation where descendant = F

H和F的祖先闭包关系很明显可以发现
F的祖先 ,F ,distance = F的祖先,H,distance+1
java 伪代码:

Long HID=999L;
Long FID = 888L;
List<NodeRelationDO> nodeSelectResult = nodeRelationDao.queryAncestor(FID);
List<NodeRelationDO> nodeRelations = new ArrayList<>();
for (NodeRelationDO item : nodeSelectResult) {
    NodeRelationDO nodeRelationDO = new NodeRelationDO();
    nodeRelationDO.setAncestor(item.getAncestor);
    nodeRelationDO.setDescendant(HID);
    nodeRelationDO.setDistance(item.getDistance + 1);
    nodeRelations.add(nodeRelationDO);           
}
nodeRelationDao.batchUpsert(nodeRelations);

2).删除:删除C节点(递归删除)
删除比较简单,主要分以下2个步骤

  • 删除节点
  • 删除节点闭包表相关数据
    图4

    红色的是需要删除的关系边(见图4),其实很容易就找出规律,需要删除和删除节点有关的所有关系边
//删除节点闭包表相关数据
delete from node_relation where ancestor in (需要删除集合) 
or  descendant in  (需要删除的集合);

3).变更父节点: 将B节点移到C节点上(见图5)


图5
  • 从节点考虑只需要把B节点parent_id 直接更新成C就完成了
  • 闭包关系表变化会复杂一些
    接下去介绍下,move过程闭包关系表的变化

1).待删除的边:

select * from node_relation where ancestor in (B的所有祖先) 
and descendant in (B树的所有节点,包含B);

删除后会得到2颗分裂的树,见图6 ,红色边就是等待删除的边

图6

2).重建关系
新增关系:(C及C的所有祖先节点) x (B树的所有节点),这两个节点集合的笛卡尔积就是新增的边(见图7)。
图7

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容