Mysql 实现 向上递归查找父节点并返回树结构
需求:通过mysql 8.0以下版本实现,一个人多角色id,一个角色对应某个节点menu_id,根节点的父节点存储为NULL, 向上递归查找父节点并返回树结构。


测试数据:
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0; -- ----------------------------
-- Table structure for Menu
-- ----------------------------
DROP TABLE IF EXISTS `Menu`;
CREATE TABLE `Menu` (
`menu_id` varchar(255) COLLATE utf8mb4_bin NOT NULL DEFAULT '0',
`sup_menu` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,
`auth_id` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,
PRIMARY KEY (`menu_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; -- ----------------------------
-- Records of Menu
-- ----------------------------
BEGIN;
INSERT INTO `Menu` VALUES ('1', NULL, '1');
INSERT INTO `Menu` VALUES ('11', NULL, '11');
INSERT INTO `Menu` VALUES ('12', '11', '12');
INSERT INTO `Menu` VALUES ('13', '11', '13');
INSERT INTO `Menu` VALUES ('14', '12', '14');
INSERT INTO `Menu` VALUES ('15', '12', '15');
INSERT INTO `Menu` VALUES ('16', '13', '16');
INSERT INTO `Menu` VALUES ('17', '13', '17');
INSERT INTO `Menu` VALUES ('2', '1', '2');
INSERT INTO `Menu` VALUES ('22', '21', '26');
INSERT INTO `Menu` VALUES ('25', '22', '25');
INSERT INTO `Menu` VALUES ('3', '1', '3');
INSERT INTO `Menu` VALUES ('4', '2', '4');
INSERT INTO `Menu` VALUES ('5', '2', '5');
INSERT INTO `Menu` VALUES ('6', '3', '6');
INSERT INTO `Menu` VALUES ('7', '3', '7');
COMMIT; SET FOREIGN_KEY_CHECKS = 1;
方法一:纯存储过程实现
1 -- 纯存储过程实现
2 DELIMITER //
3 -- 如果只有叶子,剔除掉; 如果只有根,只显示一个秃顶的根 ;如果既有叶子又有根则显示
4 DROP PROCEDURE if EXISTS query_menu_by_authid;
5 CREATE PROCEDURE query_menu_by_authid(IN roleIds varchar(1000))
6
7 BEGIN
8 -- 用于判断是否结束循环
9 declare done int default 0;
10 -- 用于存储结果集
11 declare menuid bigint;
12 declare temp_menu_ids VARCHAR(3000);
13 declare temp_sup_menus VARCHAR(3000);
14 declare return_menu_ids VARCHAR(3000);
15
16 -- 定义游标
17 declare idCur cursor for select menu_id from Menu where FIND_IN_SET(auth_id,roleIds) ;
18 -- 定义 设置循环结束标识done值怎么改变 的逻辑
19 declare continue handler for not FOUND set done = 1;
20
21
22 open idCur ;
23 FETCH idCur INTO menuid;
24 -- 临时变量存储menu_id集合
25 SET temp_menu_ids = '';
26 -- 返回存储menu_id集合
27 SET return_menu_ids = '';
28
29 WHILE done<> 1 DO
30 -- 只查找 单个 auth_id 相关的menu_id
31 -- 通过authid, 查找出menu_id, sup_menu is null
32
33 SELECT
34 GROUP_CONCAT(T2._menu_id) as t_menu_id,
35 GROUP_CONCAT(T2._sup_menu) as t_sup_menu
36 into temp_menu_ids,temp_sup_menus
37 FROM
38 (
39 SELECT
40 -- 保存当前节点。(从叶节点往根节点找,@r 保存当前到哪个位置了)。@r 初始为要找的节点。
41 -- _menu_id 当前节点
42 DISTINCT @r as _menu_id,
43 (
44 SELECT
45 CASE
46 WHEN sup_menu IS NULL THEN @r:= 'NULL'
47 ELSE @r:= sup_menu
48 END
49 FROM Menu
50 WHERE _menu_id = Menu.menu_id
51 ) AS _sup_menu,
52 -- 保存当前的Level
53 @l := @l + 1 AS level
54 FROM
55 ( SELECT @r := menuid, @l := 0
56 ) vars, Menu AS temp
57 -- 如果该节点没有父节点,则会被置为0
58 WHERE @r <> 0
59 ORDER BY @l DESC
60 ) T2
61 INNER JOIN Menu T1
62 ON T2._menu_id = T1.menu_id
63 ORDER BY T2.level DESC ;
64
65 -- 满足必须要有根节点NULL字符,则表明有根,否则不拼接给返回值
66 IF FIND_IN_SET('NULL',temp_sup_menus) > 0 THEN
67 SET return_menu_ids = CONCAT(temp_menu_ids,',',return_menu_ids);
68 END IF;
69
70 FETCH idCur INTO menuid;
71 END WHILE;
72 CLOSE idCur;
73
74 -- 返回指定menu_id 的数据集合
75 select Menu.menu_id,Menu.sup_menu,Menu.auth_id
76 FROM Menu
77 WHERE FIND_IN_SET(menu_id,return_menu_ids)
78 ORDER BY Menu.menu_id*1 ASC ;
79
80 END;
81 //
82 DELIMITER;
83
84 CALL query_menu_by_authid('5,15,25,26');
85 CALL query_menu_by_authid('5,17');
86 CALL query_menu_by_authid('5,11');
方法二:函数+存储过程实现
1 -- 函数+存储过程实现
2 -- 根据叶子节点查找所有父节点及其本身节点。如果只有叶子,剔除掉; 如果只有根,只显示一个秃顶的根 ;如果既有叶子又有根则显示.
3 DROP FUNCTION IF EXISTS `getParentList`;
4 CREATE FUNCTION `getParentList`(in_menu_id varchar(255))
5 RETURNS varchar(3000)
6 BEGIN
7 DECLARE sTemp VARCHAR(3000);
8 DECLARE sTempPar VARCHAR(3000);
9 SET sTemp = '';
10 SET sTempPar = in_menu_id;
11
12 -- 循环递归
13 WHILE sTempPar is not null DO
14 -- 判断是否是第一个,不加的话第一个会为空
15 IF sTemp != '' THEN
16 SET sTemp = concat(sTemp,',',sTempPar);
17 ELSE
18 SET sTemp = sTempPar;
19 END IF;
20 SET sTemp = concat(sTemp,',',sTempPar);
21 SELECT group_concat(sup_menu)
22 INTO sTempPar
23 FROM Menu
24 where sup_menu<>menu_id
25 and FIND_IN_SET(menu_id,sTempPar) > 0;
26 END WHILE;
27 RETURN sTemp;
28 END;
29
30
31 DELIMITER //
32 -- 如果只有叶子,剔除掉; 如果只有根,只显示一个秃顶的根 ;如果既有叶子又有根则显示
33 DROP PROCEDURE if EXISTS select_menu_by_authids ;
34 CREATE PROCEDURE select_menu_by_authids(IN roleIds varchar(3000))
35
36 BEGIN
37 -- 用于判断是否结束循环
38 declare done int default 0;
39 -- 用于存储结果集
40 declare menuid varchar(255);
41 declare set_menu_ids VARCHAR(3000);
42 -- 检查是否单叶子节点 单叶子节点 sup_menu is not null
43 -- sup_menu 是否为null
44 declare _sup_menu int default -1;
45
46 -- 定义游标
47 declare idCur cursor for select menu_id from Menu where FIND_IN_SET(auth_id,roleIds) ;
48 -- 定义 设置循环结束标识done值怎么改变 的逻辑
49 declare continue handler for not FOUND set done = 1;
50
51 OPEN idCur ;
52 FETCH idCur INTO menuid;
53 -- 临时变量存储menu_id集合
54 SET set_menu_ids = '';
55
56 WHILE done<> 1 DO
57 SELECT sup_menu
58 INTO _sup_menu
59 FROM Menu
60 WHERE FIND_IN_SET(menu_id,getParentList(menuid))
61 ORDER BY sup_menu ASC
62 LIMIT 1;
63
64 -- 查找指定角色对应的menu_id ,sup_menu is null 则说明有根,则进行拼接
65 IF _sup_menu is NULL THEN
66 SELECT CONCAT(set_menu_ids, GROUP_CONCAT(menu_id),',') INTO set_menu_ids
67 FROM Menu
68 where FIND_IN_SET(menu_id,getParentList(menuid)) ;
69 END IF;
70
71 FETCH idCur INTO menuid;
72 END WHILE;
73 CLOSE idCur;
74
75 -- 返回指定menu_id 的数据集合
76 SELECT Menu.menu_id,Menu.sup_menu,Menu.auth_id
77 FROM Menu
78 WHERE FIND_IN_SET(menu_id,set_menu_ids)
79 ORDER BY Menu.menu_id*1 ASC ;
80
81 END ;
82 //
83 DELIMITER ;
84
85 CALL select_menu_by_authids('5,15,25,26');
86 CALL select_menu_by_authids('5,17');
87 CALL select_menu_by_authids('5,11');
方法三:纯函数实现
1 -- 根据叶子节点查找所有父节点及其本身节点。如果只有叶子,剔除掉; 如果只有根,只显示一个秃顶的根 ;如果既有叶子又有根则显示.
2 DROP FUNCTION IF EXISTS `getParentLists`;
3 -- 参数1角色id 字符串逗号隔开; 参数2 角色id 个数
4 CREATE FUNCTION `getParentLists`(in_roleIds varchar(1000),count_roleIds INT)
5 RETURNS VARCHAR(3000)
6 BEGIN
7 -- 临时存放通过单个角色查找的单个menu_id
8 DECLARE sMenu_id_by_roleId VARCHAR(1000);
9 -- 临时存放通过单个角色查找的多个menu_id
10 DECLARE sMenu_ids_by_roleId VARCHAR(1000);
11 -- 临时存放通过多个角色查找的多个menu_id
12 DECLARE sMenu_ids_by_roleIds VARCHAR(1000);
13 -- 函数返回的menu_id 集合
14 DECLARE sReturn_menu_ids VARCHAR(3000);
15 -- 当前角色
16 DECLARE current_roleId_rows INT DEFAULT 0;
17
18 SET sMenu_id_by_roleId = '';
19 SET sMenu_ids_by_roleIds = '';
20 SET sReturn_menu_ids = '';
21
22 -- 循环多角色
23 WHILE current_roleId_rows < count_roleIds DO
24
25 -- 依次按角色取1条menu_id
26 SELECT menu_id
27 INTO sMenu_id_by_roleId
28 FROM Menu
29 WHERE FIND_IN_SET(auth_id, in_roleIds)
30 ORDER BY menu_id DESC
31 LIMIT current_roleId_rows, 1 ;
32
33 SET sMenu_ids_by_roleId = sMenu_id_by_roleId;
34 WHILE sMenu_ids_by_roleId IS NOT NULL DO
35
36 -- 判断是否是第一个,不加的话第一个会为空
37 IF sMenu_ids_by_roleIds != '' THEN
38 SET sMenu_ids_by_roleIds = CONCAT(sMenu_ids_by_roleIds,',',sMenu_ids_by_roleId);
39 ELSE
40 SET sMenu_ids_by_roleIds = sMenu_ids_by_roleId;
41 END IF;
42
43 -- 通过角色id 拼接 所有的父节点,重点拼接根节点,根节点置为字符NULL,用于后面判断是否有根
44 SELECT
45 GROUP_CONCAT(
46 CASE
47 WHEN sup_menu IS NULL THEN 'NULL'
48 ELSE sup_menu
49 END
50 )
51 INTO sMenu_ids_by_roleId
52 FROM Menu
53 WHERE FIND_IN_SET(menu_id,sMenu_ids_by_roleId) > 0;
54
55 END WHILE;
56 SET current_roleId_rows=current_roleId_rows+1;
57
58 -- 满足必须要有根节点NULL字符,则表明有根,否则不拼接给返回值
59 IF FIND_IN_SET('NULL',sMenu_ids_by_roleIds) > 0 THEN
60 SET sReturn_menu_ids = CONCAT(sReturn_menu_ids,',',sMenu_ids_by_roleIds);
61 END IF;
62
63 -- 清空通过单个角色查到的多个menu_id, 避免重复拼接
64 SET sMenu_ids_by_roleIds = '';
65 END WHILE;
66
67 RETURN sReturn_menu_ids;
68 END;
69
70 SELECT Menu.menu_id,Menu.sup_menu,Menu.auth_id
71 FROM Menu
72 WHERE FIND_IN_SET(menu_id, getParentLists('15,25,5,26',4))
73 ORDER BY Menu.menu_id+0 ASC;
74
75 SELECT Menu.menu_id,Menu.sup_menu,Menu.auth_id
76 FROM Menu
77 WHERE FIND_IN_SET(menu_id, getParentLists('17,5',2))
78 ORDER BY Menu.menu_id*1 ASC;
79
80 SELECT Menu.menu_id,Menu.sup_menu,Menu.auth_id
81 FROM Menu
82 WHERE FIND_IN_SET(menu_id, getParentLists('11,5',2))
83 ORDER BY Menu.menu_id*2 ASC;
Mysql 实现 向上递归查找父节点并返回树结构的更多相关文章
- mysql 递归查找菜单节点的所有子节点
背景 ...
- SQL 递归查询(根据指定的节点向上获取所有父节点,向下获取所有子节点)
--------------------01.向上查找所有父节点-----------------WITH TEMP AS (SELECT * FROM CO_Department WHERE ID= ...
- s查找父节点
查找所有的父节点,包括本身,不包括就<>id with tbs as(select * from TB_HomeBase where ID=223 union all select a.* ...
- jQuery 查找父节点 parents()与closest()
parents()由内向外,直到最高的父节点停止查找,返回的父节点是多个 closest()由内向外查找,当找到符合规则的一个,则不再查找,返回的是0或1个
- json 递归查找某个节点
一段json可能有很多的子节点,需要查询到某一个节点 用到的js是 find-in-json.js 地址是:https://gist.github.com/iwek/3924925 貌似翻|||墙才能 ...
- php中递归查找父级名称
/** * 获取所属公司 * @param array 列表 * @param $id 上级ID * @return array */ private static function get_top_ ...
- 用SelectSingleNode()方法查找xml节点一直返回null
代码使用如下 XmlNode root = xmlDoc.SelectSingleNode("Project"); 返回的root一直是null 查了xml文件中确实是有Proje ...
- JavaScript通过父节点ID递归生成JSON树
JavaScript通过父节点ID递归生成JSON树: · 实现思路:通过递归实现(第一次递归的时候查询出所有的父节点,然后通过当前父节点id不断地去查询所有子节点,直到递归完毕返回) · 代码示 ...
- c/c++叉树的创建与遍历(非递归遍历左右中,不破坏树结构)
二叉树的创建与遍历(非递归遍历左右中,不破坏树结构) 创建 二叉树的递归3种遍历方式: 1,先中心,再左树,再右树 2,先左树,再中心,再右树 3,先左树,再右树,再中心 二叉树的非递归4种遍历方式: ...
随机推荐
- mysql-安装(windows版本)与登录
安装mysql 1.MySQL版本 mysql-5.6.35-winx64.zip 2.首先解压到安装目录 3.修改配置文件 复制my-default.ini 重命名为my.ini 然后修改mysql ...
- SpringBoot配置多环境下的properties配置文件
1.新建SpringBoot项目之后,再另外创建两个properties文件 2.配置详情 主文件 dev和test文件 两者只是里面的配置信息有所不同而已,比如mysql, redis, nacos ...
- 10.5 详解Android Studio项目结构
Android项目的结构很复杂,并不像HTML项目,最简单的直接一个HTML文件就行了,相信学完上一节的同学就明白,哪怕是一个HelloWorld这样一个项目的文件可能都有几十个,所以我们需要搞清楚, ...
- NC202498 货物种类
NC202498 货物种类 题目 题目描述 某电商平台有 \(n\) 个仓库,编号从 \(1\) 到 \(n\) . 当购进某种货物的时候,商家会把货物分散的放在编号相邻的几个仓库中. 我们暂时不考虑 ...
- Tapdata “设擂招贤”携手 LeetCode 举办全球极客技术竞赛
2021年11月28日 Tapdata 专场全球极客技术竞赛将在 LeetCode 平台开赛,面向程序员"设擂招贤",打擂成功的前50名挑战者将优先获得 Tapdata 高端技 ...
- JDBC:处理事务
1.如何实现事务 如果现在希望对 A 表和 B 表同时删除某一个 id 号的记录,使这两个sql操作组成一个事务.(成功则同时成功,否则都失败) 注意:如果 B 表 建立了引用 A 表的id外键,并指 ...
- Dapr v1.8 正式发布
Dapr是一套开源.可移植的事件驱动型运行时,允许开发人员轻松立足云端与边缘位置运行弹性.微服务.无状态以及有状态等应用程序类型.Dapr能够确保开发人员专注于编写业务逻辑,而不必分神于解决分布式系统 ...
- 打印三角形及debug用法
package www.nihao; public class demo01 { public static void main(String[] args) { //打印三角形 5行 for(int ...
- 《Python编程:从入门到实践》第十八章笔记:Django最基本用法笔记
最近在看Python编程:从入门到实践,这是这本书"项目3 Web应用程序"第18章的笔记.记录了django最基本的一些日常用法,以便自己查阅. 可能是我的这本书版本比较老,书上 ...
- IO概述(概念&分类)和字节输入流+OUTputStream类&FileOutPutStream类介绍
IO概述 什么是IO 生活中,你肯定经历过这样的场景.当你编辑一个文本文件,忘记了保存,可能文件就白白编辑了.当你的电脑上插入一个U盘,可以吧一个视频,拷贝到你的电脑硬盘里,那么数据都是在哪些设备上的 ...