闭包表的增删改查(达梦数据库)
在开发中我们最常使用邻接表,像有上下级关系的数据经常会把父节点和子节点存在同一行,如果要找某个节点下所有的子节点,就必须重复递归去寻找,而且我们并不知道树的深度,随着树深度的加深,性能也会越受影响。
最近梳理闭包表,由于 修改 操作费了点时间,所以做个笔记备忘一下。
(如果哪里有误,还请提醒一下。)
介绍
闭包表是用两张表来存储数据,利用空间换时间。一张表只保存节点的信息,另外一张表保存节点之间的路径即父节点与子节点之间的距离(深度)。
表结构
CREATE TABLE "NODE"
(
"ID" VARCHAR(96) NOT NULL,
"PARENT_ID" VARCHAR(96),
"NODE_NAME" VARCHAR(300) NOT NULL,
CLUSTER PRIMARY KEY("ID")) STORAGE(ON "MAIN", CLUSTERBTR) ;
COMMENT ON TABLE "NODE" IS '节点表';
COMMENT ON COLUMN "NODE"."ID" IS '主键';
COMMENT ON COLUMN "NODE"."PARENT_ID" IS '父节点(也可不存,通过path表中的distance查找)';
COMMENT ON COLUMN "NODE"."NODE_NAME" IS '节点名称';
;
CREATE TABLE "PATH"
(
"ID" BIGINT IDENTITY(1, 1) NOT NULL,
"ANCESTOR_ID" VARCHAR(50) NOT NULL,
"DESCENDANT_ID" VARCHAR(50) NOT NULL,
"DISTANCE" BIGINT NOT NULL,
CLUSTER PRIMARY KEY("ID")) STORAGE(ON "MAIN", CLUSTERBTR) ;
COMMENT ON TABLE "PATH" IS '路径表';
COMMENT ON COLUMN "PATH"."ANCESTOR_ID" IS '祖先节点';
COMMENT ON COLUMN "PATH"."DESCENDANT_ID" IS '后代节点';
COMMENT ON COLUMN "PATH"."DISTANCE" IS '祖先节点与后代节点的距离(深度)';
COMMENT ON COLUMN "PATH"."ID" IS '主键';
数据存储
-
节点表(node)
-
路径表(path)
为了方便与图对应,路径表中的ID先用A、B、C等表示
查找
查找B的祖先节点
select ancestor_id from path where descendant_id = #{B_ID} and ancestor_id != descendant_id order by distance desc
查找B的后代节点
select descendant_id from path where ancestor_id = #{B_ID} and ancestor_id != descendant_id order by distance
增加(增加F节点为C节点的子节点)
- 首先在“节点表”里添加新节点
insert into node (parent_id, node_name) values ('C_ID','F_ID')
- 在“路径表”中添加路径关系
(1) 先添加新节点与自己间的路径
insert into path(ancestor_id, descendant_id,distance) values ('F_ID','F_ID',0)
(2) 再添加新节点与所有祖先节点之间的路径(JAVA)
//查询新节点父节点的祖先节点(包含父节点自己,也就是节点C A)
select * from path where descendant_id = #{C_ID} order by distance desc
//queryAncestor(新节点父节点的祖先节点,包含父节点自己)
List<Path> ancestorNodes = PathService.queryAncestor(node.getParentId());
//再添加新节点与所有祖先节点之间的路径
if (ancestorNodes != null) {
List<Path> paths = new ArrayList<>();
for (Path pathA : ancestorNodes) {
Path pathN = new Path();
pathN.setAncestorId(pathA.getAncestorId());
pathN.setDescendantId(node.getId());
if (pathA.getDistance() != null) {
pathN.setDistance(pathA.getDistance() + 1);
}
paths.add(pathN);
}
PathService.saveBatch(paths);
}
修改(把B节点移到C节点下)
- 首先解除B的所有祖先节点(节点A)与B及其所有后代节点(节点D、E)之间的路径
delete from path where
descendant_id in (select descendant_id from path where ancestor_id = #{B_ID})
and
ancestor_id in (select ancestor_id from path where descendant_id = #{B_ID} and ancestor_id != descendant_id)
2. 建立新路径,即B节点的新父节点及其所有祖先节点(节点C和节点A) 与 B节点及其所有后代节点(节点D、E) 间的路径(笛卡尔积)
select p.ancestor_id ancestor_id, s.descendant_id descendant_id, p.distance + s.distance + 1 distance
from path p
cross join path s
where p.descendant_id = #{C_ID} and s.ancestor_id = #{B_ID}
删除(删除节点B)
- 删除“节点表”中的B节点及B节点所有后代节点(D、E)信息
delete from node where id in (select descendant_id from path where ancestor_id = #{B_ID} and del_flag = '0')
- 删除“路径表”中节点所有后代节点(包括自己)(节点B、D、E)与祖先节点(节点A)之间的路径
delete from path where
descendant_id in (select descendant_id from path where ancestor_id = #{B_ID})