详细聊一聊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 27 MySQL
MySQL获取所有分类的前N条记录
May 07 MySQL
MySQL 不等于的三种使用及区别
Jun 03 MySQL
使用ORM新增数据在Mysql中的操作步骤
Jul 26 MySQL
一篇文章带你深入了解Mysql触发器
Aug 02 MySQL
MySQL8.0升级的踩坑历险记
Nov 01 MySQL
MySQL中一条SQL查询语句是如何执行的
Apr 08 MySQL
MySQL导致索引失效的几种情况
Jun 25 MySQL
MySQL事务的隔离级别详情
Jul 15 MySQL
MySQL性能指标TPS+QPS+IOPS压测
Aug 05 MySQL
MySQL实现用逗号进行拼接、以逗号进行分割
Dec 24 MySQL
mysql查询结果实现多列拼接查询
Apr 03 #MySQL
mysql使用instr达到in(字符串)的效果
数据分析数据库ClickHouse在大数据领域应用实践
Apr 03 #MySQL
一文了解MYSQL三大范式和表约束
MYSQL优化之数据表碎片整理详解
Innodb存储引擎中的后台线程详解
Apr 03 #MySQL
MySQL磁盘碎片整理实例演示
You might like
无线电的诞生过程
2021/03/01 无线电
虹吸壶煮咖啡26个注意事项
2021/03/03 冲泡冲煮
phpmyadmin 常用选项设置详解版
2010/03/07 PHP
Windows平台实现PHP连接SQL Server2008的方法
2017/07/26 PHP
最近项目写了一些js,水平有待提高
2009/01/31 Javascript
为指定元素增加样式的js代码
2009/12/09 Javascript
深入理解JavaScript系列(1) 编写高质量JavaScript代码的基本要点
2012/01/15 Javascript
jquery取消选择select下拉框示例代码
2014/02/22 Javascript
JS动态修改图片的URL(src)的方法
2015/04/01 Javascript
jQuery实现鼠标滑向当前图片高亮显示并且其它图片变灰的方法
2015/07/27 Javascript
原生js模拟淘宝购物车项目实战
2015/11/18 Javascript
浅谈js中子页面父页面方法 变量相互调用
2016/08/04 Javascript
原生JS实现圣旨卷轴展开效果
2017/03/06 Javascript
bing Map 在vue项目中的使用详解
2018/04/09 Javascript
对Vue2 自定义全局指令Vue.directive和指令的生命周期介绍
2018/08/30 Javascript
layui的layedit富文本赋值方法
2019/09/18 Javascript
Python写的Socks5协议代理服务器
2014/08/06 Python
python实现的重启关机程序实例
2014/08/21 Python
Python中的rjust()方法使用详解
2015/05/19 Python
python爬取51job中hr的邮箱
2016/05/14 Python
python 随机数使用方法,推导以及字符串,双色球小程序实例
2017/09/12 Python
深入解析python中的实例方法、类方法和静态方法
2019/03/11 Python
原来我一直安装 Python 库的姿势都不对呀
2019/11/11 Python
CSS3中线性颜色渐变的一些实现方法
2015/07/14 HTML / CSS
联想哥伦比亚网上商城:Lenovo Colombia
2017/01/10 全球购物
指针和引用有什么区别
2013/01/13 面试题
视光学专业毕业生推荐信
2013/10/28 职场文书
学校安全教育制度
2014/01/31 职场文书
面试后感谢信怎么写
2014/02/01 职场文书
大学新闻系应届生求职信
2014/06/02 职场文书
门市房租房协议书
2014/12/04 职场文书
2014七年级班主任工作总结
2014/12/05 职场文书
店铺转让协议书
2015/01/29 职场文书
春季运动会加油词
2015/07/18 职场文书
【海涛dota】偶遇拉娜娅 质量局德鲁伊第一视角解说
2022/04/01 DOTA
以下牛机,你有几个
2022/04/05 无线电