网站首页 > 教程文章 正文
最近在开发大量文件存储,标记,检索功能的时候遇到存储结构的选择,一个是树结构,一个是列表结构。平常用列表结构存储数据比较多,不熟悉在数据库中存储树结构的方式,因此做了以下分析。
在使用数据库存储树结构时,有多种设计方法。常见的方式包括 邻接列表法(Adjacency List)、路径枚举法(Path Enumeration / Materialized Path)、嵌套集法(Nested Set)、闭包表法(Closure Table)。这几种方式各有优劣:
树结构存储方式
1.邻接列表法(Adjacency List)
表结构
CREATE TABLE nodes (
id INTEGER PRIMARY KEY,
parent_id INTEGER, -- 指向父节点id,如果为NULL就是根节点
name TEXT
);
示例数据
id | parent_id | name |
1 | NULL | Root |
2 | 1 | Child1 |
3 | 1 | Child2 |
4 | 2 | SubChild |
优缺点
- 优点:简单、易理解,增删改方便。
- 缺点:查找某节点的所有祖先/后代较繁琐,要多次查询。
2.路径枚举法(Materialized Path)
节点保存一条路径字符串。
表结构
CREATE TABLE nodes (
id INTEGER PRIMARY KEY,
path TEXT, -- 例如 '/1/2/4/' 表示根到本节点的完整路径
name TEXT
);
查询举例
- 查询某节点的所有子孙:SELECT * FROM nodes WHERE path LIKE '/1/2/%'
- 查询某节点的所有祖先,需分割path。
优缺点
- 优点:后代/祖先查找效率高。
- 缺点:增删节点时,可能批量更新子节点path。
3.嵌套集法(Nested Set)
用 left/right 数值区间表示层级关系。
表结构
CREATE TABLE nodes (
id INTEGER PRIMARY KEY,
lft INTEGER,
rgt INTEGER,
name TEXT
);
查询举例
- 查询所有子孙:SELECT * FROM nodes WHERE lft > ? AND rgt < ?
- 维护复杂,增删节点需整体调整区间。
优缺点
- 优点:查询整棵子树非常高效。
- 缺点:插入/删除性能低,实现复杂。
4.闭包表法(Closure Table)
单独建一张关系表,记录所有祖先-后代关系。
CREATE TABLE nodes (
id INTEGER PRIMARY KEY,
name TEXT
);
CREATE TABLE node_closure (
ancestor_id INTEGER,
descendant_id INTEGER
);
查询举例
- 优点:后代/祖先查找简单高效,结构灵活。
- 缺点:维护多一张表,插入/删除时要做多行修改。
树结构存储推荐实践
最常用的是 邻接列表法,其表设计简单,适合大多数业务场景。若需高效查询整棵子树,推荐使用“路径枚举”或“闭包表”。
树结构和列表结构优劣势
1.列表结构
优点
- 简单易懂:结构扁平,设计和实现都很简单。
- 查询高效:大多数情况下一张表就可以完成常规的增、删、查、改操作,SQL语句简单,性能较好。
- 维护方便:数据维护和管理相对容易,适合大部分无层级需求的业务场景。
缺点
- 无法表达层级关系:对于需要层次结构的数据(如组织架构、分类树),表达能力有限,只能匮乏地表现。
- 无法实现递归操作:如查找某个元素的所有子孙元素、祖先元素等会非常困难,需要反复查询。
2.树形结构
常见的实现方式
- 父子节点(Parent-Child)模式:每条记录有一个parent_id,指向自己的父节点。
- 路径枚举(Path Enumeration):每条记录记录从根到自身的全路径,如A/B/C。
- 嵌套集(Nested Set):左右值算法preorder、postorder遍历树时编号,方便整棵子树的查询。
优点
- 表达层级关系:很适合表达有上下级或树状关系的数据,如菜单、组织架构等。
- 灵活性强:可以轻松支持无限层级的结构。
- 支持递归查询:尤其在支持WITH RECURSIVE或CONNECT BY的数据库中,递归查询祖先、子孙非常方便。
缺点
- 查询复杂:对比列表结构,查询层级关系时SQL相对复杂,性能有所下降,尤其树高度较大时。
- 维护成本高:某些实现(如嵌套集)对于插入、删除、移动节点操作维护成本较高。
- 性能问题:深层树结构可能导致递归查询性能较差。
3.总结
- 如果业务上没有层级关系,只需扁平展示数据,列表结构更为高效、简单。
- 如果必须表现数据的上下级、层级关系(如菜单、分类树),树形结构更合适,但需要特殊的表设计和更复杂的SQL支持。
因此在此次的功能中,使用列表结构更为合适,理由如下:一是业务本身树形结构展示不需要太频繁,文件之间的上下级关系并不常见,反而通过检索方式查找更常用。二是列表形式更适用于各类打标签和时间、优先级等方式的排序。
- 上一篇: 如何避免 JS 内存泄漏?(js如何解决内存泄露)
- 下一篇: 前端开发中79条不可忽视的知识点汇总
猜你喜欢
- 2025-07-27 8个前端面试的题目(前端面试题2020及答案 知乎)
- 2025-07-27 深入理解Node.js中的垃圾回收和内存泄漏的捕获
- 2025-07-27 网易+腾讯+阿里,前端工程师面经!30K果然不是很好拿
- 2025-07-27 go errgroup 获取gorouting错误信息
- 2025-07-27 盛趣游戏unity客户端面试(盛趣游戏招聘岗位)
- 2025-07-27 Swift 性能探索和优化分析(swift运行效率)
- 2025-07-27 「前端开发」eval() 函数认知和学习以及注意事项
- 2025-07-27 解锁C++灵魂:函数指针场景及实例(c++函数指针和指针函数)
- 2025-07-27 2021 年 Node.js 开发人员学习路线图
- 2025-07-27 跨越十年的C++演进:C++11新特性全解析
- 最近发表
- 标签列表
-
- location.href (44)
- document.ready (36)
- git checkout -b (34)
- 跃点数 (35)
- 阿里云镜像地址 (33)
- qt qmessagebox (36)
- mybatis plus page (35)
- vue @scroll (38)
- 堆栈区别 (33)
- 什么是容器 (33)
- sha1 md5 (33)
- navicat导出数据 (34)
- 阿里云acp考试 (33)
- 阿里云 nacos (34)
- redhat官网下载镜像 (36)
- srs服务器 (33)
- pico开发者 (33)
- https的端口号 (34)
- vscode更改主题 (35)
- 阿里云资源池 (34)
- os.path.join (33)
- redis aof rdb 区别 (33)
- 302跳转 (33)
- http method (35)
- js array splice (33)