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

如果只有叶子,剔除掉; 如果只有根,只显示一个秃顶的根 ;如果既有叶子又有根则显示叶子与根。
测试数据:
如果 传入角色ID【auth_id】:   5,15,25,26,则只查找5,15的所有父节点,因为25,26无根节点

测试数据:

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 实现 向上递归查找父节点并返回树结构的更多相关文章

  1. mysql 递归查找菜单节点的所有子节点

    背景                                                                                                   ...

  2. SQL 递归查询(根据指定的节点向上获取所有父节点,向下获取所有子节点)

    --------------------01.向上查找所有父节点-----------------WITH TEMP AS (SELECT * FROM CO_Department WHERE ID= ...

  3. s查找父节点

    查找所有的父节点,包括本身,不包括就<>id with tbs as(select * from TB_HomeBase where ID=223 union all select a.* ...

  4. jQuery 查找父节点 parents()与closest()

    parents()由内向外,直到最高的父节点停止查找,返回的父节点是多个 closest()由内向外查找,当找到符合规则的一个,则不再查找,返回的是0或1个

  5. json 递归查找某个节点

    一段json可能有很多的子节点,需要查询到某一个节点 用到的js是 find-in-json.js 地址是:https://gist.github.com/iwek/3924925 貌似翻|||墙才能 ...

  6. php中递归查找父级名称

    /** * 获取所属公司 * @param array 列表 * @param $id 上级ID * @return array */ private static function get_top_ ...

  7. 用SelectSingleNode()方法查找xml节点一直返回null

    代码使用如下 XmlNode root = xmlDoc.SelectSingleNode("Project"); 返回的root一直是null 查了xml文件中确实是有Proje ...

  8. JavaScript通过父节点ID递归生成JSON树

    JavaScript通过父节点ID递归生成JSON树: · 实现思路:通过递归实现(第一次递归的时候查询出所有的父节点,然后通过当前父节点id不断地去查询所有子节点,直到递归完毕返回)   · 代码示 ...

  9. c/c++叉树的创建与遍历(非递归遍历左右中,不破坏树结构)

    二叉树的创建与遍历(非递归遍历左右中,不破坏树结构) 创建 二叉树的递归3种遍历方式: 1,先中心,再左树,再右树 2,先左树,再中心,再右树 3,先左树,再右树,再中心 二叉树的非递归4种遍历方式: ...

随机推荐

  1. 国外价值10K+美金的Python面试题,珍藏已久,含泪放了出来

    兄弟们,没吹牛皮,一哥们在国外面试的时候,就是要他做的这个,直接给他说,做出来了给你15K(单位是刀),做不出来就拜拜~ 大兄弟当时就不服了,这不是看不起我么,分分钟就给整完了~ 我上我也行系列: 唠 ...

  2. AspectJ和AOP细节

    AspectJ1. 简介 AspectJ:Java社区里最完整最流行的AOP框架.(在Spring中AOP是一种思想,而AspectJ是一种AOP的更明确具体实现) 在Spring2.0以上版本中,可 ...

  3. PostgreSQL的查询技巧: 零除, GENERATED STORED, COUNT DISTINCT, JOIN和数组LIKE

    零除的处理 用NULLIF(col, 0)可以避免复杂的WHEN...CASE判断, 例如 ROUND(COUNT(view_50.amount_in)::NUMERIC / NULLIF(COUNT ...

  4. Effective Java 3 读后感

    Effective Java 3 读后感 最近学习了一下Effectvie Java,这是一本非常适合有一定经验的Java后端人员阅读的书.书中总结许多编码经验对开发很有帮助,比如其中总结的对于流和L ...

  5. 攻防世界MISC—进阶区32—37

    32.normal_png 得到一张png,扔进kali中binwalk 和 pngcheck一下,发现CRC报错 尝试修改图片高度,我是把height的2改为4,得到flag 33.很普通的数独 得 ...

  6. STM32与物联网01-ESP8266基本操作

    ESP8266物联网简介 ESP8266简介 ESP8266 是上海乐鑫公司开发的一款具有 WiFi 功能的控制芯片,它带有完整的 TCP/IP 协议栈,因此可以用作物联网开发. ESP8266 本身 ...

  7. 使用 Azure 静态 Web 应用服务免费部署 Hexo 博客

    一.前言 最近在折腾 Hexo 博客,试了一下 Azure 的静态 Web 应用服务,发现特别适合静态文档类型的网站,而且具有免费额度,支持绑定域名.本文只是以 Hexo 作为示例,其他类型的框架也是 ...

  8. 一个小 Trick

    平方变两次 一个状态 \(S\) 有一个贡献,所有状态 \(S\) 组成集合 \(U\) . 然后我们要统计下面这个东西 \[ans=\sum_{S\in U}f^2(S) \] 然后我们就可以看作是 ...

  9. vue-cli 启动项目时空白页面

    vue-cli 启动项目时空白页面 在启动项目时 npm run serve / npm run dev 启动 vue 项目空白页:且终端及控制台都未报错 通过各种查阅发现在项目根目录中 vue-co ...

  10. 皮皮调度(1)——从Airflow到DolphinScheduler,以及“皮皮调度”的来历

    按照前一篇文章 <GraalVM -- 让Java变得再次强大> 末尾提到的计划,本来这篇文章是想写一下GraalVM的后续<深耕云原生的Java应用框架 -- Quarkus> ...