背景
日常需求开发过程中,相信大家一定会遇到树形结构的数据,如企业的组织架构、文件夹等形式的数据,那么如何在关系型数据库中设计其表结构,来储存这种树状数据,从而减少开发党的代码量,提高日常开发效率。
概括
通过步步深入的方式,不断地对表结构进行优化与设计,让大家更加深刻的了解其原理。
1、邻接表模型(递归)
CREATE TABLE `tree_node` (
`id` varchar(32) COLLATE utf8mb4_bin NOT NULL COMMENT '主键',
`party_id` varchar(32) COLLATE utf8mb4_bin NOT NULL COMMENT '所属团体id,party表id',
`parent_id` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '父节点id',
`name` varchar(256) COLLATE utf8mb4_bin NOT NULL COMMENT '名称',
`number` varchar(128) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '冗余账号',
`type` smallint NOT NULL COMMENT '节点类型 0:非叶子节点;1:叶子节点',
`create_time` bigint NOT NULL COMMENT '创建时间',
`modify_time` bigint NOT NULL COMMENT '修改时间',
`creator_id` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '创建人',
`modifier_id` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '最后修改人',
`is_deleted` tinyint NOT NULL DEFAULT '0' COMMENT '删除标志位'
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='树节点'
只储存树节点的parent_id(父节点ID)
优点: 表结构非常简单,但在实际使用场景中作用不大。
缺点: DDDD,比如某想要某个节点下所有数据,得用递归的方式来查,等等。
2、路径枚举法模型
-- 树节点表结构(路径枚举法)
CREATE TABLE `tree_node` (
`id` varchar(32) COLLATE utf8mb4_bin NOT NULL COMMENT '主键',
`parent_id` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '父节点id',
`name` varchar(256) COLLATE utf8mb4_bin NOT NULL COMMENT '名称',
`path` varchar(512) COLLATE utf8mb4_bin NOT NULL COMMENT '路径',
`type` smallint NOT NULL COMMENT '节点类型 0:非叶子节点;1:叶子节点',
`short_id` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT 'uuid压缩为8位的数据'
`create_time` bigint NOT NULL COMMENT '创建时间',
`modify_time` bigint NOT NULL COMMENT '修改时间',
`creator_id` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '创建人',
`modifier_id` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '最后修改人',
`is_deleted` tinyint NOT NULL DEFAULT '0' COMMENT '删除标志位',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='树节点'
示例数据如下
| id | parent_id | name | type | path | short_id |
|---|---|---|---|---|---|
| 1 | -- | 中心 | 0 | /4030eac2 | 4030eac2 |
| 2 | 1 | 中心底下部门A | 0 | /4030eac2/ff24cce8 | ff24cce8 |
| 3 | 1 | 中心底下部门B | 0 | /4030eac2/e2f8abc2 | e2f8abc2 |
| 4 | 2 | 部门A的人C | 1 | /4030eac2/ff24cce8/b7d687f1 | b7d687f1 |
| 5 | 3 | 部门B的人D | 1 | /4030eac2/e2f8abc2/c7bc4b5c | c7bc4b5c |
多存储树节点在树中的路径,唯一ID,两个数据
优点: 在查找树节点等操作上,更加方便、快捷。
缺点: 当某个节点发生移动时,其底下的子节点的记录的路径均要发生改变,就意味着需要修改大量数据。
快捷操作示例
查询某个节点下的所有节点数据。
select * FROM tree_node tn WHERE tn.path like '某节点的path%'
优点:使用like '某节点的path%'(左匹配),还可以走索引。一条语句就可以查出数据,还super快。
示例小结: 其他还有可以有很多操作,比如删除某个节点下所有数据等,都可以围绕节点的path来进行操作。
3、实战应用(以企业组织树为例)
场景说明
在实际开发过程中,组织树的数据,肯定是有多种数据的,如部门、人等,如何设计tree_node表结构,来增加效率与性能?
解决思路
- 如果有很多个对象的话,那在tree_node表中来储存这些对象的属性,肯定是不合适的。那我们可以添加一个字段data_id,来储存对应的数据的ID,如user表的ID,这样比如要查询树下,这个人的数据,只要拿着data_id去user表中查对应的数据即可。
- tree_node中应该只存放简单的数据,如name等简单的数据,方便进行展示,提高效率。
- 在tree_node表中添加data_type字段用于区别不同对象,来增加tree_node表的复用性,如果有多种树形结构的数据,使用data_type进行区分。
- 为方便查找数据,也可以再冗余一个字段parent_data_id(父节点的data_id)。
最终表结构
CREATE TABLE `tree_node` (
`id` varchar(32) COLLATE utf8mb4_bin NOT NULL COMMENT '主键',
`parent_id` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '父节点id',
`tree_node_index` bigint NOT NULL COMMENT '该分组下的位序',
`path` varchar(512) COLLATE utf8mb4_bin NOT NULL COMMENT '路径',
`short_id` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT 'uuid压缩为8位的数据',
`type` smallint NOT NULL COMMENT '节点类型 0:非叶子节点;1:叶子节点',
`data_id` varchar(32) COLLATE utf8mb4_bin NOT NULL COMMENT '节点数据id',
`data_type` smallint NOT NULL COMMENT '节点数据类型',
`parent_data_id` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '父节点数据',
`name` varchar(256) COLLATE utf8mb4_bin NOT NULL COMMENT '名称',
`name_pinyin_for_search` varchar(512) COLLATE utf8mb4_bin NOT NULL COMMENT '名称全拼、简拼等,主要用于搜索',
`create_time` bigint NOT NULL COMMENT '创建时间',
`modify_time` bigint NOT NULL COMMENT '修改时间',
`creator_id` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '创建人',
`modifier_id` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '最后修改人',
`is_deleted` tinyint NOT NULL DEFAULT '0' COMMENT '删除标志位',
PRIMARY KEY (`id`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='树节点数据'
备注:欢迎大佬们指点下这种设计方式存在的不足之处。
总结
1、简单来说这种方式的思维也就是用空间换时间。多冗余一些数据,方便查询与展示。