详细聊一聊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 infobright的安装步骤
Apr 07 MySQL
MySQL sql_mode的使用详解
May 08 MySQL
通过shell脚本对mysql的增删改查及my.cnf的配置
Jul 07 MySQL
mysql sum(if())和count(if())的用法说明
Jan 18 MySQL
千万级用户系统SQL调优实战分享
Mar 03 MySQL
解决MySQL Varchar 类型尾部空格的问题
Apr 06 MySQL
MySQL中一条SQL查询语句是如何执行的
Apr 08 MySQL
Windows 64位 安装 mysql 8.0.28 图文教程
Apr 19 MySQL
提高系统的吞吐量解决数据库重复写入问题
Apr 23 MySQL
Mysql 一主多从的部署
May 20 MySQL
Mysql数据库事务的脏读幻读及不可重复读详解
May 30 MySQL
MySQL中JOIN连接的基本用法实例
Jun 05 MySQL
mysql查询结果实现多列拼接查询
Apr 03 #MySQL
mysql使用instr达到in(字符串)的效果
数据分析数据库ClickHouse在大数据领域应用实践
Apr 03 #MySQL
一文了解MYSQL三大范式和表约束
MYSQL优化之数据表碎片整理详解
Innodb存储引擎中的后台线程详解
Apr 03 #MySQL
MySQL磁盘碎片整理实例演示
You might like
PHP 常见郁闷问题答解
2006/11/25 PHP
php遍历目录viewDir函数
2009/12/15 PHP
解析PHP实现多进程并行执行脚本
2013/06/18 PHP
div li的多行多列 无刷新分页示例代码
2013/10/16 PHP
PHP与MYSQL中UTF8编码的中文排序实例
2014/10/21 PHP
PHP编写学校网站上新生注册登陆程序的实例分享
2016/03/21 PHP
Laravel框架使用Redis的方法详解
2018/05/30 PHP
Windows Live的@live.com域名注册漏洞 利用代码
2006/12/27 Javascript
js获取上传文件大小示例代码
2014/04/10 Javascript
JavaScript中Cookie操作实例
2015/01/09 Javascript
JavaScript编写连连看小游戏
2015/07/07 Javascript
JavaScript实现可拖拽的拖动层Div实例
2015/08/05 Javascript
使用JQuery实现的分页插件分享
2015/11/05 Javascript
jquery实现折叠菜单效果【推荐】
2017/03/08 Javascript
jQuery实现文章图片弹出放大效果
2017/04/06 jQuery
JavaScript实现简单的四则运算计算器完整实例
2017/04/28 Javascript
Angular 4依赖注入学习教程之ValueProvider的使用(七)
2017/06/04 Javascript
简单的三步vuex入门
2018/05/20 Javascript
vue element动态渲染、移除表单并添加验证的实现
2019/01/16 Javascript
多种类型jQuery网页验证码插件代码实例
2021/01/09 jQuery
[26:50]2018完美盛典DOTA2表演赛
2018/12/17 DOTA
Python使用正则匹配实现抓图代码分享
2015/04/02 Python
浅谈python内置变量-reversed(seq)
2017/06/21 Python
Python针对给定字符串求解所有子序列是否为回文序列的方法
2018/04/21 Python
Python使用matplotlib实现的图像读取、切割裁剪功能示例
2018/04/28 Python
python进程间通信Queue工作过程详解
2019/11/01 Python
Python求解正态分布置信区间教程
2019/11/20 Python
意大利宠物用品购物网站:Bauzaar
2018/09/15 全球购物
财务人员求职自荐书范文
2014/02/10 职场文书
经典英文广告词
2014/03/18 职场文书
超市理货员岗位职责
2014/07/04 职场文书
辞旧迎新演讲稿
2014/09/15 职场文书
安全员岗位职责
2015/02/10 职场文书
学风建设主题班会
2015/08/17 职场文书
2015年社区反邪教工作总结
2015/10/14 职场文书
《艾尔登法环》Boss腐烂树灵很有可能是《黑暗之魂3》的一个废案
2022/04/11 其他游戏