详细聊一聊mysql的树形结构存储以及查询


Posted in MySQL onApril 05, 2022

本文主要研究一下mysql的树形结构存储及查询

存储parent

这种方式就是每个节点存储自己的parent_id信息

  • 建表及数据准备
CREATE TABLE `menu` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL,
  `parent_id` int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;

INSERT INTO `menu` (`id`, `name`, `parent_id`) VALUES
(1, 'level1a',  0),
(2, 'level1b', 0),
(3, 'level2a-1a',1),
(4, 'level2b-1a',1),
(5, 'level2a-1b', 2),
(6, 'level2b-1b', 2),
(7, 'level3-2a1a', 3),
(8, 'level3-2b1a', 4),
(9, 'level3-2a1b', 5),
(10, 'level3-2b1b', 6);
  • 查询
-- 查询跟节点下的所有节点
SELECT t1.name AS lev1, t2.name as lev2, t3.name as lev3
FROM menu AS t1
LEFT JOIN menu AS t2 ON t2.parent_id = t1.id
LEFT JOIN menu AS t3 ON t3.parent_id = t2.id
WHERE t1.name = 'level1a';

+---------+------------+-------------+
| lev1    | lev2       | lev3        |
+---------+------------+-------------+
| level1a | level2a-1a | level3-2a1a |
| level1a | level2b-1a | level3-2b1a |
+---------+------------+-------------+

-- 查询叶子节点
SELECT t1.name FROM
menu AS t1 LEFT JOIN menu as t2
ON t1.id = t2.parent_id
WHERE t2.id IS NULL;

+-------------+
| name        |
+-------------+
| level3-2a1a |
| level3-2b1a |
| level3-2a1b |
| level3-2b1b |
+-------------+

存储及修改上比较方便,就是要在sql里头查询树比较费劲,一般是加载到内存由应用自己构造

存储path

这种方式在存储parent的基础上,额外存储path,即从根节点到该节点的路径

  • 建表及数据准备
CREATE TABLE `menu_path` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL,
  `parent_id` int(11) NOT NULL DEFAULT '0',
  `path` varchar(255) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;

INSERT INTO `menu_path` (`id`, `name`, `parent_id`, `path`) VALUES
(1, 'level1a', 0, '1/'),
(2, 'level1b', 0, '2/'),
(3, 'level2a-1a',1, '1/3'),
(4, 'level2b-1a',1, '1/4'),
(5, 'level2a-1b', 2, '2/5'),
(6, 'level2b-1b', 2, '2/6'),
(7, 'level3-2a1a', 3, '1/3/7'),
(8, 'level3-2b1a', 4, '1/4/8'),
(9, 'level3-2a1b', 5, '2/5/9'),
(10, 'level3-2b1b', 6, '2/6/10');
  • 查询
-- 查询某个节点的所有子节点
select * from menu_path where path like '1/%'
+----+-------------+-----------+-------+
| id | name        | parent_id | path  |
+----+-------------+-----------+-------+
| 1  | level1a     | 0         | 1/    |
| 3  | level2a-1a  | 1         | 1/3   |
| 4  | level2b-1a  | 1         | 1/4   |
| 7  | level3-2a1a | 3         | 1/3/7 |
| 8  | level3-2b1a | 4         | 1/4/8 |
+----+-------------+-----------+-------+

查找某个节点及其子节点比较方面,就是修改比较费劲,特别是节点移动,所有子节点的path都得跟着修改

MPTT(Modified Preorder Tree Traversal)

详细聊一聊mysql的树形结构存储以及查询

不存储parent_id,改为存储lft,rgt,它们的值由树的先序遍历顺序决定

  • 建表及数据准备
CREATE TABLE `menu_preorder` (
  `id` int(11) NOT NULL,
  `name` varchar(50) NOT NULL,
  `lft` int(11) NOT NULL DEFAULT '0',
  `rgt` int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;

                   1(level1a)14
         2(level2a)7                8(level2b)13
3(level3a-2a)4 5(level3b-2a)6 9(level3c-2b)10 11(level3d-2b)12

INSERT INTO `menu_preorder` (`id`, `name`, `lft`, `rgt`) VALUES
(1, 'level1a', 1, 14),
(2, 'level2a',2, 7),
(3, 'level2b',8, 13),
(4, 'level3a-2a', 3, 4),
(5, 'level3b-2a', 5, 6),
(6, 'level3c-2b', 9, 10),
(7, 'level3d-2b', 11, 12);

select * from menu_preorder
+----+------------+-----+-----+
| id | name       | lft | rgt |
+----+------------+-----+-----+
| 1  | level1a    | 1   | 14  |
| 2  | level2a    | 2   | 7   |
| 3  | level2b    | 8   | 13  |
| 4  | level3a-2a | 3   | 4   |
| 5  | level3b-2a | 5   | 6   |
| 6  | level3c-2b | 9   | 10  |
| 7  | level3d-2b | 11  | 12  |
+----+------------+-----+-----+
  • 查询
-- 查询某个节点及其子节点,比如level2b
select * from menu_preorder where lft between 8 and 13
+----+------------+-----+-----+
| id | name       | lft | rgt |
+----+------------+-----+-----+
| 3  | level2b    | 8   | 13  |
| 6  | level3c-2b | 9   | 10  |
| 7  | level3d-2b | 11  | 12  |
+----+------------+-----+-----+

-- 查询所有叶子节点
SELECT name
FROM menu_preorder
WHERE rgt = lft + 1;

+------------+
| name       |
+------------+
| level3a-2a |
| level3b-2a |
| level3c-2b |
| level3d-2b |
+------------+

-- 查询某个节点及其父节点
SELECT parent.*
FROM menu_preorder AS node,
menu_preorder AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
AND node.name = 'level2b'
ORDER BY parent.lft;

+----+---------+-----+-----+
| id | name    | lft | rgt |
+----+---------+-----+-----+
| 1  | level1a | 1   | 14  |
| 3  | level2b | 8   | 13  |
+----+---------+-----+-----+

-- 树形结构展示
SELECT CONCAT( REPEAT(' ', COUNT(parent.name) - 1), node.name) AS name
FROM menu_preorder AS node,
menu_preorder AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
GROUP BY node.name
ORDER BY node.lft;

+--------------+
| name         |
+--------------+
| level1a      |
|  level2a     |
|   level3a-2a |
|   level3b-2a |
|  level2b     |
|   level3c-2b |
|   level3d-2b |
+--------------+

好处是通过lft进行范围(该节点的lft,rgt作为范围)查找就可以,缺点就是增删节点导致很多节点的lft及rgt都要修改

小结

  • 存储parent的方式最为场景,一般树形结构数据量不大的话,直接在应用层内存构造树形结构和搜索
  • 存储path的好处是可以借助path来查找节点及其子节点,缺点就是移动node需要级联所有子节点的path,比较费劲
  • MPTT的方式好处是通过lft进行范围(该节点的lft,rgt作为范围)查找就可以,缺点就是增删节点导致很多节点的lft及rgt都要修改

doc

到此这篇关于mysql树形结构存储以及查询的文章就介绍到这了,更多相关mysql树形结构存储及查询内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

MySQL 相关文章推荐
多属性、多分类MySQL模式设计
Apr 05 MySQL
mysql查询的控制语句图文详解
Apr 11 MySQL
mysql的MVCC多版本并发控制的实现
Apr 14 MySQL
jdbc使用PreparedStatement批量插入数据的方法
Apr 27 MySQL
MySQL下使用Inplace和Online方式创建索引的教程
May 26 MySQL
浅谈MySQL之浅入深出页原理
Jun 23 MySQL
Mysql排序的特性详情
Nov 01 MySQL
MYSQL 运算符总结
Nov 11 MySQL
一次Mysql update sql不当引起的生产故障记录
Apr 01 MySQL
mysql使用 not int 子查询隐含陷阱
Apr 12 MySQL
pt-archiver 主键自增
Apr 26 MySQL
Mysql 文件配置解析介绍
May 06 MySQL
mysql查询结果实现多列拼接查询
Apr 03 #MySQL
mysql使用instr达到in(字符串)的效果
数据分析数据库ClickHouse在大数据领域应用实践
Apr 03 #MySQL
一文了解MYSQL三大范式和表约束
MYSQL优化之数据表碎片整理详解
Innodb存储引擎中的后台线程详解
Apr 03 #MySQL
MySQL磁盘碎片整理实例演示
You might like
php格式输出文件var_export函数实例
2014/11/15 PHP
PHP积分兑换接口实例
2015/02/09 PHP
PHP实现GIF图片验证码
2015/11/04 PHP
扩展JS Date对象时间格式化功能的小例子
2013/12/02 Javascript
JS实现静止元素自动移动示例
2014/04/14 Javascript
Node.js中使用socket创建私聊和公聊聊天室
2015/11/19 Javascript
jquery实现移动端点击图片查看大图特效
2020/09/11 Javascript
微信小程序 图片宽高自适应详解
2017/05/11 Javascript
javascript获取指定区间范围随机数的方法
2017/09/08 Javascript
基于JavaScript中字符串的match与replace方法(详解)
2017/12/04 Javascript
解决Nodejs全局安装模块后找不到命令的问题
2018/05/15 NodeJs
vue组件间通信六种方式(总结篇)
2019/05/15 Javascript
vue中keep-alive组件的入门使用教程
2019/06/06 Javascript
vue实现在v-html的html字符串中绑定事件
2019/10/28 Javascript
javascript中layim之查找好友查找群组
2021/02/06 Javascript
Python中的zip函数使用示例
2015/01/29 Python
Python基础知识_浅谈用户交互
2017/05/31 Python
从头学Python之编写可执行的.py文件
2017/11/28 Python
Python使用正则表达式获取网页中所需要的信息
2018/01/29 Python
Python实现判断一行代码是否为注释的方法
2018/05/23 Python
python matlibplot绘制3D图形
2018/07/02 Python
python+pyqt5实现24点小游戏
2019/01/24 Python
python实现kNN算法识别手写体数字的示例代码
2019/08/16 Python
如何基于python实现脚本加密
2019/12/28 Python
GNC健安喜美国官网:美国第一营养品牌
2016/07/22 全球购物
建筑工程实习自我鉴定
2013/09/19 职场文书
中学自我评价
2014/01/31 职场文书
大学信息公开实施方案
2014/03/09 职场文书
销售主管竞聘书
2014/03/31 职场文书
优秀三好学生事迹材料
2014/08/31 职场文书
2014年局领导班子自身建设情况汇报
2014/11/21 职场文书
2015年党务工作者个人工作总结
2015/10/22 职场文书
2016廉洁教育心得体会
2016/01/20 职场文书
2019年度开业庆典祝福语大全!
2019/07/05 职场文书
成功的商业计划书这样写才最靠谱
2019/07/12 职场文书
Java实现二分搜索树的示例代码
2022/03/17 Java/Android