详细聊一聊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数字类型自增的坑
May 07 MySQL
MySQL 查询速度慢的原因
May 25 MySQL
MySQL中distinct与group by之间的性能进行比较
May 26 MySQL
MySQL 8.0 Online DDL快速加列的相关总结
Jun 02 MySQL
MySQL中IF()、IFNULL()、NULLIF()、ISNULL()函数的使用详解
Jun 26 MySQL
Mysql中调试存储过程最简单的方法
Jun 30 MySQL
MySQL 8.0 驱动与阿里druid版本兼容问题解决
Jul 01 MySQL
MySQL中连接查询和子查询的问题
Sep 04 MySQL
MySQL数据库索引的最左匹配原则
Nov 20 MySQL
MySQL中rank() over、dense_rank() over、row_number() over用法介绍
Mar 23 MySQL
MySQL中一条SQL查询语句是如何执行的
Apr 08 MySQL
mysql使用FIND_IN_SET和group_concat两个方法查询上下级机构
Apr 20 MySQL
mysql查询结果实现多列拼接查询
Apr 03 #MySQL
mysql使用instr达到in(字符串)的效果
数据分析数据库ClickHouse在大数据领域应用实践
Apr 03 #MySQL
一文了解MYSQL三大范式和表约束
MYSQL优化之数据表碎片整理详解
Innodb存储引擎中的后台线程详解
Apr 03 #MySQL
MySQL磁盘碎片整理实例演示
You might like
判“新”函数:得到今天与明天的秒数
2006/10/09 PHP
ThinkPHP项目分组配置方法分析
2016/03/23 PHP
PHP图片水印类的封装
2017/07/06 PHP
PHP PDOStatement::fetchObject讲解
2019/02/01 PHP
qTip 基于JQuery的Tooltip插件[兼容性好]
2010/09/01 Javascript
深入解析contentWindow, contentDocument
2013/07/04 Javascript
Javascript全局变量var与不var的区别深入解析
2013/12/09 Javascript
分享20个提升网站界面体验的jQuery插件
2014/12/15 Javascript
详解JavaScript逻辑And运算符
2015/12/04 Javascript
浅谈angular2的http请求返回结果的subcribe注意事项
2017/03/01 Javascript
javascript DOM的详解及实例代码
2017/03/06 Javascript
JavaScript循环_动力节点Java学院整理
2017/06/28 Javascript
javascript 初学教程及五子棋小程序的简单实现
2017/07/04 Javascript
使用AngularJS对表单提交内容进行验证的操作方法
2017/07/12 Javascript
jqueryUI tab标签页代码分享
2017/10/09 jQuery
vue中axios处理http发送请求的示例(Post和get)
2017/10/13 Javascript
bootstrap 点击空白处popover弹出框隐藏实例
2018/01/24 Javascript
Vue.js 动态为img的src赋值方法
2018/03/14 Javascript
JS实现显示当前日期的实例代码
2018/07/03 Javascript
vue 引用自定义ttf、otf、在线字体的方法
2019/05/09 Javascript
layui动态加载多表头的实例
2019/09/05 Javascript
2分钟实现一个Vue实时直播系统的示例代码
2020/06/05 Javascript
[06:42]DOTA2每周TOP10 精彩击杀集锦vol.1
2014/06/25 DOTA
python中的多线程实例教程
2014/08/27 Python
python检测远程udp端口是否打开的方法
2015/03/14 Python
Python3 读、写Excel文件的操作方法
2018/10/20 Python
详解Python做一个名片管理系统
2019/03/14 Python
Python模块常用四种安装方式
2020/10/20 Python
Django-simple-captcha验证码包使用方法详解
2020/11/28 Python
HTML5 Canvas的常用线条属性值总结
2016/03/17 HTML / CSS
主治医师岗位职责
2013/12/10 职场文书
营销人才自我鉴定范文
2013/12/25 职场文书
公务员培训心得体会
2013/12/28 职场文书
鼓舞士气的口号
2014/06/16 职场文书
不同意离婚答辩状
2015/05/22 职场文书
pytorch model.cuda()花费时间很长的解决
2021/06/01 Python