0
点赞
收藏
分享

微信扫一扫

还在用递归?一文带你了解mysql数据库层面的树状查询



文章目录

  • 前言
  • 一、在数据库层面的树状数据查询
  • 二、使用步骤
  • 1.创建表,添加数据
  • 2.利用mysql8 的新特性 自带的递归--查找父级集合
  • 注意
  • 3.利用mysql8 的新特性 自带的递归--查找所有子集
  • 总结


前言

开发过程中遇到树状数据的查询有很多,例如常见的就是菜单的树状查询

一般情况下,这种子父关系的数据,解决办法如下几种:
1 我们可以在程序中,查询所有菜单数据,然后通过递归调用,达到一个树状数据组装的目的,或者查询一个节点下的所有子集(递归子集),或者查找一个节点的所有父级集合;
2 我们可以通过在表中设计多个字段, 例如 path (代表该节点的路径,3节点的路径,如: 1,2,3) 等办法,维护这样的一个字段,也可以快速定位到子集或者父级,那么这里有个成本,就是要多维护这个字段
3 我们可以写函数,达到通过一个节点查找到子集或者父级的目的

今天带大家了解的方法以上三种都不是!!!

这里面其实第二种方法很不错,查询子集 可以通过path 用 like 查询; 查找父级,可以通过path 包含处理;
这种方法也是我平时用的最多的,如果大家对此有疑问,或者对此感兴趣而不会用,请留言,我之后可以在补一期这样的技术博文;

一、在数据库层面的树状数据查询

要想使用此种方法,那么就需要mysql 的版本为8.0,我的数据库版本

还在用递归?一文带你了解mysql数据库层面的树状查询_java

二、使用步骤

1.创建表,添加数据

表以及如下(示例):

DROP TABLE IF EXISTS `auth_menu`;

CREATE TABLE `auth_menu` (
  `id` int NOT NULL AUTO_INCREMENT COMMENT '菜单id',
  `menu_name` varchar(55) DEFAULT NULL COMMENT '菜单名称',
  `menu_code` varchar(55) DEFAULT NULL COMMENT '菜单编码',
  `menu_level` int DEFAULT '1' COMMENT '菜单级别',
  `menu_icon` varchar(55) DEFAULT NULL COMMENT '菜单图标',
  `menu_url` varchar(255) DEFAULT NULL COMMENT '菜单地址',
  `menu_pid` int DEFAULT '0' COMMENT '菜单父id',
  `menu_type` int DEFAULT '1' COMMENT '菜单类型 1菜单 2按钮',
  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
  `remark` varchar(55) DEFAULT NULL COMMENT '备注',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=85 DEFAULT CHARSET=utf8mb3 COMMENT='用户信息';

/*Data for the table `auth_menu` */

insert  into `auth_menu`(`id`,`menu_name`,`menu_code`,`menu_level`,`menu_icon`,`menu_url`,`menu_pid`,`menu_type`,`create_time`,`update_time`,`remark`) values 
(0,'顶级','',0,'','',NULL,1,'2022-08-03 09:00:00','2022-09-22 11:15:34','ADMIN,USER'),
(7,'我的图谱','graph',1,'HistoryOutlined','/MyGraph',0,1,'2022-08-03 09:00:00','2022-09-16 00:42:14','ADMIN,USER'),
(8,'创建','graph:insert',2,NULL,NULL,7,2,'2022-08-03 09:00:03','2022-08-06 02:35:35','ADMIN'),
(9,'删除','graph:delete',2,NULL,NULL,7,2,'2022-08-03 09:00:05','2022-08-06 02:35:37','ADMIN'),
(10,'编辑','graph:update',2,NULL,NULL,7,2,'2022-08-03 09:00:08','2022-08-06 02:35:43','ADMIN'),
(11,'模式设计','model',1,'AppstoreAddOutlined','/SchemaDesign',0,1,'2022-08-03 09:09:21','2022-09-16 00:42:24','ADMIN,USER'),
(12,'实体类型设计','model:etype',2,NULL,'/SchemaDesign/EntityTypeDesign',11,1,'2022-08-03 09:09:23','2022-08-06 02:36:02','ADMIN,USER'),
(13,'创建','model:etype:insert',3,NULL,NULL,12,2,'2022-08-03 09:09:25','2022-08-06 02:35:52','ADMIN'),
(14,'编辑','model:etype:update',3,NULL,NULL,12,2,'2022-08-03 09:09:27','2022-08-06 02:36:05','ADMIN'),
(15,'删除','model:etype:delete',3,NULL,NULL,12,2,'2022-08-03 09:09:30','2022-08-06 02:36:10','ADMIN'),
(16,'关系类型设计','model:rtype',2,NULL,'/SchemaDesign/RelationshipTypeDesign',11,1,'2022-08-03 09:21:49','2022-08-06 02:34:54','ADMIN,USER'),
(17,'创建','model:rtype:insert',3,NULL,NULL,16,2,'2022-08-03 09:21:51','2022-08-06 02:36:14','ADMIN'),
(18,'编辑','model:rtype:update',3,NULL,NULL,16,2,'2022-08-03 09:21:53','2022-08-06 02:36:17','ADMIN'),
(19,'删除','model:rtype:delete',3,NULL,NULL,16,2,'2022-08-03 09:21:56','2022-08-06 02:38:48','ADMIN'),
(20,'索引设置','model:index',2,NULL,'/SchemaDesign/IndexDesign',11,1,'2022-08-03 09:21:58','2022-08-06 02:34:58','ADMIN,USER'),
(21,'关系设计','model:relation',2,NULL,'/SchemaDesign/relationType',11,1,'2022-08-03 09:22:01','2022-08-06 02:34:59','ADMIN,USER'),
(22,'关系绑定','model:relation:insert',3,NULL,NULL,21,2,'2022-08-03 09:22:03','2022-08-06 02:38:56','ADMIN'),
(23,'删除','model:relation:delete',3,NULL,NULL,21,2,'2022-08-03 09:22:05','2022-08-06 02:39:00','ADMIN'),
(24,'可视化设计','model:visual',2,NULL,'/SchemaDesign/visualDesgin',11,1,'2022-08-03 09:22:07','2022-08-06 02:35:02','ADMIN,USER'),
(25,'图谱编辑','gedit',1,'FormOutlined','/GraphEdit',0,1,'2022-08-03 09:22:09','2022-09-16 00:43:01','ADMIN,USER'),
(26,'实体编辑','gedit:eedit',2,NULL,'/GraphEdit/EntityEdit',25,1,'2022-08-03 09:22:12','2022-08-06 02:39:29','ADMIN,USER'),
(27,'添加实体','gedit:eedit:insert',3,NULL,NULL,26,2,'2022-08-03 09:22:14','2022-08-06 02:39:47','ADMIN'),
(28,'编辑','gedit:eedit:update',3,NULL,NULL,26,2,'2022-08-03 09:22:16','2022-08-06 02:39:48','ADMIN'),
(29,'删除','gedit:eedit:delete',3,NULL,NULL,26,2,'2022-08-03 09:22:18','2022-08-06 02:39:52','ADMIN'),
(30,'关系编辑','gedit:redit',2,NULL,'/GraphEdit/RelationshipEdit',25,1,'2022-08-03 09:22:19','2022-08-06 02:35:09','ADMIN,USER'),
(31,'添加关系','gedit:redit:insert',3,NULL,NULL,30,2,'2022-08-03 09:22:24','2022-08-06 02:39:53','ADMIN'),
(32,'编辑','gedit:redit:update',3,NULL,NULL,30,2,'2022-08-03 09:22:26','2022-08-06 02:39:54','ADMIN'),
(33,'删除','gedit:redit:delete',3,NULL,NULL,30,2,'2022-08-03 09:22:28','2022-08-06 02:39:58','ADMIN'),
(34,'图谱探索','gexplore',1,'UnorderedListOutlined','/KnowledgeGraph',0,1,'2022-08-03 09:22:30','2022-09-16 00:43:07','ADMIN,USER'),
(35,'知识问答','kexplain',1,'CheckCircleOutlined','/KnowledgeExplain',0,1,'2022-08-03 09:22:32','2022-09-16 00:43:23','ADMIN,USER'),
(36,'归因分析','atrbanls',1,'TableOutlined','/AttributionAnalysis',0,1,'2022-08-03 09:22:34','2022-09-16 00:43:30','ADMIN,USER'),
(38,'用户管理','usermgt',1,'UserOutlined','/UserManage',0,1,'2022-08-05 01:46:27','2022-09-20 05:34:48',''),
(39,'添加','usermgt:insert',2,NULL,NULL,38,2,'2022-08-05 01:46:37','2022-08-06 02:40:30',''),
(40,'编辑','usermgt:update',2,NULL,NULL,38,2,'2022-08-06 02:24:56','2022-08-08 00:40:21',''),
(41,'删除','usermgt:delete',2,NULL,NULL,38,2,'2022-08-06 02:25:02','2022-08-06 02:40:28',''),
(42,'角色管理','rolemgt',1,'UsergroupAddOutlined','/RoleManager',0,1,'2022-08-06 02:25:08','2022-09-20 16:08:15',''),
(43,'添加','rolemgt:insert',2,NULL,NULL,42,2,'2022-08-06 02:25:41','2022-08-06 02:40:26',''),
(44,'编辑','rolemgt:update',2,NULL,NULL,42,2,'2022-08-06 02:25:50','2022-08-08 00:40:28',''),
(45,'删除','rolemgt:delete',2,NULL,NULL,42,2,'2022-08-06 02:26:25','2022-08-06 02:40:24',''),
(65,'文件导入','gedit:file',2,NULL,'/GraphEdit/FileUpLoadManager',25,1,'2022-09-01 01:16:19','2022-09-08 00:59:32','ADMIN'),
(66,'文件上传','gedit:file:upload',3,NULL,NULL,65,2,'2022-09-07 01:24:38','2022-09-07 01:31:08','ADMIN'),
(67,'文件删除','gedit:file:filedelete',3,NULL,NULL,65,2,'2022-09-07 01:24:47','2022-09-07 01:32:13','ADMIN'),
(68,'模板下载','gedit:file:down',3,NULL,NULL,65,2,'2022-09-07 01:24:56','2022-09-07 01:42:57','ADMIN'),
(69,'导入数据','gedit:file:import',3,NULL,NULL,65,2,'2022-09-07 01:26:19','2022-09-07 01:32:47','ADMIN'),
(70,'日志删除','gedit:file:logdelete',3,NULL,NULL,65,2,'2022-09-07 01:29:34','2022-09-07 01:32:59','ADMIN'),
(71,'菜单管理','menu',1,'TableOutlined','/MenuManager',0,1,'2022-09-16 00:39:59','2022-09-20 09:19:46',NULL),
(72,'查询','graph:view',2,'','',7,2,'2022-09-16 15:26:28','2022-09-20 05:44:12','ADMIN,USER'),
(73,'查询','model:etype:view',3,'','',12,2,'2022-09-16 15:46:44','2022-09-20 05:44:12','ADMIN,USER'),
(74,'查询','model:rtype:view',3,'','',16,2,'2022-09-16 15:46:54','2022-09-20 05:44:14','ADMIN,USER'),
(75,'查询','model:relation:view',3,'','',21,2,'2022-09-16 15:47:05','2022-09-20 05:44:14','ADMIN,USER'),
(76,'查询','gedit:eedit:view',3,'','',26,2,'2022-09-16 15:47:15','2022-09-20 05:44:16','ADMIN,USER'),
(77,'查询','gedit:redit:view',3,'','',30,2,'2022-09-16 15:47:28','2022-09-20 05:44:18','ADMIN,USER'),
(78,'查询','gedit:file:view',3,'','',65,2,'2022-09-16 15:47:42','2022-09-20 05:45:25','ADMIN'),
(79,'查询','usermgt:view',2,'','',38,2,'2022-09-16 15:48:06','2022-09-20 05:45:30',''),
(80,'查询','rolemgt:view',2,'','',42,2,'2022-09-16 15:48:16','2022-09-20 05:45:32',''),
(83,'申请管理','apply',1,'AuditOutlined','/ApplyManager',0,1,'2022-09-19 16:47:56','2022-09-20 16:07:53','');

2.利用mysql8 的新特性 自带的递归–查找父级集合

查找一个节点以及所有父级,代码如下(示例):

WITH recursive t(id, menu_pid, menu_name,n) AS 
( 
SELECT id, menu_pid, menu_name,1
FROM auth_menu WHERE id = 13 

UNION ALL 
SELECT m.id, m.menu_pid,m. menu_name,n+1
FROM `auth_menu` m JOIN t ON m.`id` = t.menu_pid
WHERE n<3
) 
SELECT m.id, m.menu_pid,m.menu_name
 FROM `auth_menu` m JOIN t ON m.`id` = t.id;

注意

还在用递归?一文带你了解mysql数据库层面的树状查询_mysql_02


先解释下这个查询的意思:

就是查找一个节点的顶级节点有哪些,包括他自身节点;

1 WITH recursive t, t 就是相当于一个临时表的概念,

2 ON m.id = t.menu_pid 这个关联条件的意思是 查询菜单中id 为13 的父菜单的 id

3 WITH recursive 开始自身递归,知道找到顶级节点为止,这样 这个t中就包含了 13,以及13 的所有父级id

4 然后外面的关联就是一个正常的关联查询了

5 可以看到这里面有个n ,这个是用来控制 递归次数的,也就是说不能无线递归

如果不增加这个n 递归次数没那么会报错

错误代码: 3636
Recursive query aborted after 1000001 iterations. Try increasing @@cte_max_recursion_depth to a larger value.

3.利用mysql8 的新特性 自带的递归–查找所有子集

WITH recursive t(id, menu_pid, menu_name,n) AS 
( 
SELECT id, menu_pid, menu_name,1
FROM auth_menu WHERE id = 0

UNION ALL 
SELECT m.id, m.menu_pid,m. menu_name,n+1
FROM `auth_menu` m JOIN t ON m.menu_pid = t.id
WHERE n<20
) 
SELECT m.id, m.menu_pid,m.menu_name,t.id
 FROM `auth_menu` m JOIN t ON m.id = t.id;

查询结果如下:

还在用递归?一文带你了解mysql数据库层面的树状查询_java_03


其实就是相当于查询到了所有,因为我的这里面顶级节点id就是0;

与上一个查询不同的地方在于:

FROM `auth_menu` m JOIN t ON m.menu_pid = t.id

也很好理解,这里是要查找子集,所以在已知这个节点id的情况下,只要找到父级id与该id相同的数据就可以了,所以是 ON m.menu_pid = t.id

总结

由于mysql8.0带来的新特性,可以让我们有了一个更好的方法,在很少的字段(仅有id pid)的情况下,可以不用传统的递归方式,实现递归查询。
优势进步的一天,希望与君共勉!! 加油


举报

相关推荐

0 条评论