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种遍历方式: ...
随机推荐
- RPA应用场景-对公账户开户资质审查
场景概述 对公账户开户资质审查 所涉系统名称 人民银行账户管理系统 人工操作(时间/次) 0.5小时 所涉人工数量 132 操作频率 不定时 场景流程 1.机器人自动登录人民银行账户管理系统 2.查询 ...
- C++ 练气期之一文看懂字符串
C++ 练气期之细聊字符串 1. 概念 程序不仅仅用于数字计算,现代企业级项目中更多流转着充满了烟火气的人间话语.这些话语,在计算机语言称为字符串. 从字面上理解字符串,类似于用一根竹签串起了很多字符 ...
- Java开发学习(七)----DI依赖注入之自动装配与集合注入
一.自动配置 上一篇博客花了大量的时间把Spring的注入去学习了下,总结起来就两个字麻烦.麻烦在配置文件的编写配置上.那有更简单方式么?有,自动配置 1.1 依赖自动装配 IoC容器根据bean所依 ...
- HMS Core 机器学习服务打造同传翻译新“声”态,AI让国际交流更顺畅
2022年6月,HMS Core机器学习服务面向开发者提供一项全新的开放能力--同声传译,通过AI语音技术减少资源成本,加强沟通交流,旨在帮助开发者制作丰富多样的同声传译应用. HMS Core同声传 ...
- Tapdata 肖贝贝:实时数据引擎系列(六)-从 PostgreSQL 实时数据集成看增量数据缓存层的必要性
摘要:对于 PostgreSQL 的实时数据采集, 业界经常遇到了包括:对源库性能/存储影响较大, 采集性能受限, 时间回退重新同步不支持, 数据类型较复杂等等问题.Tapdata 在解决 Pos ...
- AOP-动态代理
动态代理的原理代理设计模式的原理:使用一个代理将原本对象包装起来,然后用该代理对象"取代"原始对象.任何对原始对象的调用都要通过代理.代理对象决定是否以及何时将方法调用转到原始对象 ...
- zookeeper Caused by: java.lang.IllegalArgumentException: myid file is missing
zookeeper 安装集群,配置文件和data和其它都没有问题. 则需要在data里面下添加一个myid 文件 myid里面的数据个service一致 比如.cnf里面配置是 dataDir=/us ...
- 算法竞赛进阶指南0x33同余
定义 如果整数a,b除以正整数m的余数相同,那么a,b模m同余 . 知识点 拓展欧几里得算法 代码 #include <bits/stdc++.h> using namespace std ...
- python打开文件、文件夹窗口、终端窗口
简介 在一些项目中,我们会需要在生成完文件后打开某些文件或者文件夹窗口,这就需要使用到内置的文件打开方式了. 打开文件或文件夹 Windows import os import subprocess ...
- 清北学堂 2020 国庆J2考前综合强化 Day2
目录 1. 题目 T1 一 题目描述 Sol T2 二 题目描述 Sol T3 三 题目描述 Sol T4 四 题目描述 Sol 2. 算法 -- 数据结构 1. 题目 T1 一 题目描述 问题描述 ...