MySQL 实现树形的遍历(关于多级菜单栏以及多级上下部门的查询问题)

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到教程。

前言:
        关于多级别菜单栏或者权限系统中部门上下级的树形遍历,oracle中有connect by来实现,mysql没有这样的便捷途径,所以MySQL遍历数据表是我们经常会遇到的头痛问题,下面通过存储过程来实现。



1,建立测试表和数据:
DROP TABLE IF EXISTS csdn.channel;   
CREATE TABLE csdn.channel (   
  id INT(11) NOT NULL AUTO_INCREMENT,     
  cname VARCHAR(200) DEFAULT NULL,   
  parent_id INT(11) DEFAULT NULL,   
  PRIMARY KEY (id)   
) ENGINE=INNODB DEFAULT CHARSET=utf8;   
INSERT  INTO channel(id,cname,parent_id)    
VALUES (13,'首页',-1),   
       (14,'TV580',-1),   
       (15,'生活580',-1),   
       (16,'左上幻灯片',13),   
       (17,'帮忙',14),   
       (18,'栏目简介',17);  
DROP TABLE IF EXISTS channel;


2,利用临时表和递归过程实现树的遍历(mysql的UDF不能递归调用):

2.1,从某节点向下遍历子节点,递归生成临时表数据  
-- pro_cre_childlist
DELIMITER $$     
DROP PROCEDURE IF EXISTS csdn.pro_cre_childlist$$   
CREATE PROCEDURE csdn.pro_cre_childlist(IN rootId INT,IN nDepth INT)   
BEGIN   
      DECLARE done INT DEFAULT 0;   
      DECLARE b INT;   
      DECLARE cur1 CURSOR FOR SELECT id FROM channel WHERE parent_id=rootId;   
      DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;   
      SET max_sp_recursion_depth=12;   
       
      INSERT INTO tmpLst VALUES (NULL,rootId,nDepth);   
       
      OPEN cur1;   
       
      FETCH cur1 INTO b;   
      WHILE done=0 DO   
              CALL pro_cre_childlist(b,nDepth+1);   
              FETCH cur1 INTO b;   
      END WHILE;   
       
      CLOSE cur1;   
END$$   

2.2,从某节点向上追溯根节点,递归生成临时表数据  
-- pro_cre_parentlist
DELIMITER $$
DROP PROCEDURE IF EXISTS csdn.pro_cre_parentlist$$   
CREATE PROCEDURE csdn.pro_cre_parentlist(IN rootId INT,IN nDepth INT)   
BEGIN   
      DECLARE done INT DEFAULT 0;   
      DECLARE b INT;   
      DECLARE cur1 CURSOR FOR SELECT parent_id FROM channel WHERE id=rootId;   
      DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;   
      SET max_sp_recursion_depth=12;   
       
      INSERT INTO tmpLst VALUES (NULL,rootId,nDepth);   
       
      OPEN cur1;   
       
      FETCH cur1 INTO b;   
      WHILE done=0 DO   
              CALL pro_cre_parentlist(b,nDepth+1);   
              FETCH cur1 INTO b;   
      END WHILE;   
       
      CLOSE cur1;   
     END$$   
  

2.3,实现类似Oracle SYS_CONNECT_BY_PATH的功能,递归过程输出某节点id路径  
-- pro_cre_pathlist
DELIMITER $$
USE csdn$$
DROP PROCEDURE IF EXISTS pro_cre_pathlist$$
CREATE PROCEDURE pro_cre_pathlist(IN nid INT,IN delimit VARCHAR(10),INOUT pathstr VARCHAR(1000))
BEGIN                     
      DECLARE done INT DEFAULT 0;   
      DECLARE parentid INT DEFAULT 0;         
      DECLARE cur1 CURSOR FOR    
      SELECT t.parent_id,CONCAT(CAST(t.parent_id AS CHAR),delimit,pathstr)   
        FROM channel AS t WHERE t.id = nid;   
           
      DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;   
      SET max_sp_recursion_depth=12;                     
       
      OPEN cur1;   
       
      FETCH cur1 INTO parentid,pathstr;   
      WHILE done=0 DO              
              CALL pro_cre_pathlist(parentid,delimit,pathstr);   
              FETCH cur1 INTO parentid,pathstr;   
      END WHILE;   
            
      CLOSE cur1;    
END$$


DELIMITER ;


2.4,递归过程输出某节点name路径  
-- pro_cre_pnlist
DELIMITER $$
USE csdn$$
DROP PROCEDURE IF EXISTS pro_cre_pnlist$$
CREATE PROCEDURE pro_cre_pnlist(IN nid INT,IN delimit VARCHAR(10),INOUT pathstr VARCHAR(1000))
BEGIN                     
      DECLARE done INT DEFAULT 0;   
      DECLARE parentid INT DEFAULT 0;         
      DECLARE cur1 CURSOR FOR    
      SELECT t.parent_id,CONCAT(t.cname,delimit,pathstr)   
        FROM channel AS t WHERE t.id = nid;   
           
      DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;   
      SET max_sp_recursion_depth=12;                     
       
      OPEN cur1;   
       
      FETCH cur1 INTO parentid,pathstr;   
      WHILE done=0 DO              
              CALL pro_cre_pnlist(parentid,delimit,pathstr);   
              FETCH cur1 INTO parentid,pathstr;   
      END WHILE;   
            
      CLOSE cur1;    
     END$$


DELIMITER ;


2.5,调用函数输出id路径   
-- fn_tree_path
DELIMITER $$ 
DROP FUNCTION IF EXISTS csdn.fn_tree_path$$   
CREATE FUNCTION csdn.fn_tree_path(nid INT,delimit VARCHAR(10)) RETURNS VARCHAR(2000) CHARSET utf8   
BEGIN     
  DECLARE pathid VARCHAR(1000);   
     
  SET @pathid=CAST(nid AS CHAR);   
  CALL pro_cre_pathlist(nid,delimit,@pathid);   
     
  RETURN @pathid;   
END$$   
  
  
2.6,调用函数输出name路径  
-- fn_tree_pathname
-- 调用函数输出name路径   
DELIMITER $$ 
DROP FUNCTION IF EXISTS csdn.fn_tree_pathname$$   
CREATE FUNCTION csdn.fn_tree_pathname(nid INT,delimit VARCHAR(10)) RETURNS VARCHAR(2000) CHARSET utf8   
BEGIN     
  DECLARE pathid VARCHAR(1000);   
  SET @pathid='';       
  CALL pro_cre_pnlist(nid,delimit,@pathid);   
  RETURN @pathid;   
END$$  
DELIMITER ; 
  
2.7,调用过程输出子节点   
-- pro_show_childLst  
DELIMITER $$
-- 调用过程输出子节点   
DROP PROCEDURE IF EXISTS pro_show_childLst$$   
CREATE PROCEDURE pro_show_childLst(IN rootId INT)   
BEGIN   
      DROP TEMPORARY TABLE IF EXISTS tmpLst;   
      CREATE TEMPORARY TABLE IF NOT EXISTS tmpLst    
       (sno INT PRIMARY KEY AUTO_INCREMENT,id INT,depth INT);         
       
      CALL pro_cre_childlist(rootId,0);   
       
      SELECT channel.id,CONCAT(SPACE(tmpLst.depth*2),'--',channel.cname) NAME,channel.parent_id,tmpLst.depth,fn_tree_path(channel.id,'/') path,fn_tree_pathname(channel.id,'/') pathname   
      FROM tmpLst,channel WHERE tmpLst.id=channel.id ORDER BY tmpLst.sno;   
     END$$   
     

2.8,调用过程输出父节点   
-- pro_show_parentLst
DELIMITER $$
-- 调用过程输出父节点   
DROP PROCEDURE IF EXISTS `pro_show_parentLst`$$   
CREATE PROCEDURE `pro_show_parentLst`(IN rootId INT)   
BEGIN   
      DROP TEMPORARY TABLE IF EXISTS tmpLst;   
      CREATE TEMPORARY TABLE IF NOT EXISTS tmpLst    
       (sno INT PRIMARY KEY AUTO_INCREMENT,id INT,depth INT);         
       
      CALL pro_cre_parentlist(rootId,0);   
      SELECT channel.id,CONCAT(SPACE(tmpLst.depth*2),'--',channel.cname) NAME,channel.parent_id,tmpLst.depth,fn_tree_path(channel.id,'/') path,fn_tree_pathname(channel.id,'/') pathname   
      FROM tmpLst,channel WHERE tmpLst.id=channel.id ORDER BY tmpLst.sno;   
     END$$   


 
3,开始测试:
3.1,从根节点开始显示,显示子节点集合: 

mysql> CALL pro_show_childLst(-1); 
+----+-----------------------+-----------+-------+-------------+----------------------------+
| id | NAME                  | parent_id | depth | path        | pathname                   |
+----+-----------------------+-----------+-------+-------------+----------------------------+
| 13 |   --首页              |        -1 |     1 | -1/13       | 首页/                      |
| 16 |     --左上幻灯片      |        13 |     2 | -1/13/16    | 首页/左上幻灯片/           |
| 14 |   --TV580             |        -1 |     1 | -1/14       | TV580/                     |
| 17 |     --帮忙            |        14 |     2 | -1/14/17    | TV580/帮忙/                |
| 18 |       --栏目简介      |        17 |     3 | -1/14/17/18 | TV580/帮忙/栏目简介/       |
| 15 |   --生活580           |        -1 |     1 | -1/15       | 生活580/                   |
+----+-----------------------+-----------+-------+-------------+----------------------------+
6 rows in set (0.05 sec)


Query OK, 0 rows affected (0.05 sec)


3.2,显示首页下面的子节点
CALL pro_show_childLst(13);  
mysql> CALL pro_show_childLst(13);   
+----+---------------------+-----------+-------+----------+-------------------------+
| id | NAME                | parent_id | depth | path     | pathname                |
+----+---------------------+-----------+-------+----------+-------------------------+
| 13 | --首页              |        -1 |     0 | -1/13    | 首页/                   |
| 16 |   --左上幻灯片      |        13 |     1 | -1/13/16 | 首页/左上幻灯片/        |
+----+---------------------+-----------+-------+----------+-------------------------+
2 rows in set (0.02 sec)


Query OK, 0 rows affected (0.02 sec)


mysql> 


3.3,显示TV580下面的所有子节点
CALL pro_show_childLst(14);   
mysql> CALL pro_show_childLst(14);  
+----+--------------------+-----------+-------+-------------+----------------------------+
| id | NAME               | parent_id | depth | path        | pathname                   |
+----+--------------------+-----------+-------+-------------+----------------------------+
| 14 | --TV580            |        -1 |     0 | -1/14       | TV580/                     |
| 17 |   --帮忙           |        14 |     1 | -1/14/17    | TV580/帮忙/                |
| 18 |     --栏目简介     |        17 |     2 | -1/14/17/18 | TV580/帮忙/栏目简介/       |
+----+--------------------+-----------+-------+-------------+----------------------------+
3 rows in set (0.02 sec)


Query OK, 0 rows affected (0.02 sec)


mysql> 


3.4,“帮忙”节点有一个子节点,显示出来:
CALL pro_show_childLst(17);   
mysql> CALL pro_show_childLst(17); 
+----+------------------+-----------+-------+-------------+----------------------------+
| id | NAME             | parent_id | depth | path        | pathname                   |
+----+------------------+-----------+-------+-------------+----------------------------+
| 17 | --帮忙           |        14 |     0 | -1/14/17    | TV580/帮忙/                |
| 18 |   --栏目简介     |        17 |     1 | -1/14/17/18 | TV580/帮忙/栏目简介/       |
+----+------------------+-----------+-------+-------------+----------------------------+
2 rows in set (0.03 sec)


Query OK, 0 rows affected (0.03 sec)


mysql> 


3.5,“栏目简介”没有子节点,所以只显示最终节点:
mysql> CALL pro_show_childLst(18);   
+----+----------------+-----------+-------+-------------+----------------------------+
| id | NAME           | parent_id | depth | path        | pathname                   |
+----+----------------+-----------+-------+-------------+----------------------------+
| 18 | --栏目简介     |        17 |     0 | -1/14/17/18 | TV580/帮忙/栏目简介/       |
+----+----------------+-----------+-------+-------------+----------------------------+
1 row in set (0.36 sec)


Query OK, 0 rows affected (0.36 sec)


mysql> 
  
3.6,显示根节点的父节点  
CALL pro_show_parentLst(-1);   
mysql> CALL pro_show_parentLst(-1);
Empty set (0.01 sec)


Query OK, 0 rows affected (0.01 sec)


mysql>


3.7,显示“首页”的父节点
CALL pro_show_parentLst(13);   
mysql> CALL pro_show_parentLst(13);   
+----+----------+-----------+-------+-------+----------+
| id | NAME     | parent_id | depth | path  | pathname |
+----+----------+-----------+-------+-------+----------+
| 13 | --首页   |        -1 |     0 | -1/13 | 首页/    |
+----+----------+-----------+-------+-------+----------+
1 row in set (0.02 sec)


Query OK, 0 rows affected (0.02 sec)


mysql> 


3.8,显示“TV580”的父节点,parent_id为-1
CALL pro_show_parentLst(14);   
mysql> CALL pro_show_parentLst(14);   
+----+---------+-----------+-------+-------+----------+
| id | NAME    | parent_id | depth | path  | pathname |
+----+---------+-----------+-------+-------+----------+
| 14 | --TV580 |        -1 |     0 | -1/14 | TV580/   |
+----+---------+-----------+-------+-------+----------+
1 row in set (0.02 sec)


Query OK, 0 rows affected (0.02 sec)


3.9,显示“帮忙”节点的父节点
mysql>
CALL pro_show_parentLst(17);   
mysql> CALL pro_show_parentLst(17);   
+----+-----------+-----------+-------+----------+---------------+
| id | NAME      | parent_id | depth | path     | pathname      |
+----+-----------+-----------+-------+----------+---------------+
| 17 | --帮忙    |        14 |     0 | -1/14/17 | TV580/帮忙/   |
| 14 |   --TV580 |        -1 |     1 | -1/14    | TV580/        |
+----+-----------+-----------+-------+----------+---------------+
2 rows in set (0.02 sec)


Query OK, 0 rows affected (0.02 sec)


mysql>


3.10,显示最低层节点“栏目简介”的父节点
CALL pro_show_parentLst(18);  
mysql> CALL pro_show_parentLst(18);  
+----+----------------+-----------+-------+-------------+----------------------------+
| id | NAME           | parent_id | depth | path        | pathname                   |
+----+----------------+-----------+-------+-------------+----------------------------+
| 18 | --栏目简介     |        17 |     0 | -1/14/17/18 | TV580/帮忙/栏目简介/       |
| 17 |   --帮忙       |        14 |     1 | -1/14/17    | TV580/帮忙/                |
| 14 |     --TV580    |        -1 |     2 | -1/14       | TV580/                     |
+----+----------------+-----------+-------+-------------+----------------------------+
3 rows in set (0.02 sec)


Query OK, 0 rows affected (0.02 sec)


mysql> 

 

 

 

 

 

原文:http://blog.csdn.net/mchdba/article/details/39277301
参考网址: 
http://jan.kneschke.de/projects/mysql/sp/sp_tree.sql
http://blog.csdn.net/ylqmf/article/details/5172901

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/450152.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

git使用—rebase还是merge

转载自:https://segmentfault.com/q/1010000007704573/ 我猜现实中的情况是这样的: 使用 git 的人群中,不会用 rebase(哪怕是基础功能的)的至少一半(这个估计恐怕很保守了) 剩下一半里真正理解…

淘宝网轮播图

转载于:https://www.cnblogs.com/wxwxwx/p/10264370.html

atob和btoa的趣谈

2019独角兽企业重金招聘Python工程师标准>>> 不了解的人突然看到window对象的atob和btoa 函数,估计会认为哪个臭小子添加全局函数了。 你如果告诉他这是原生函数,他一定会怒骂:哪个脑残给api起个这样的名子。 你能猜出来这两个函数…

esp32使用lvgl,给图片取模显示图片

使用LVGL官方工具。 https://lvgl.io/tools/imageconverter 上传图片,如果想要透明效果,那么选择 输出格式C array,点击Convert进行转换。 下载.c文件放置到工程下使用即可。

音视频即时通讯二次开发

用于二次开发企业即时通讯源码(C/MFC),现在,做一个普通的即时通讯软件不是一件难事,网上有很多很好的这类软件的源代码可以拿来使用。今天就介绍一个我用过,觉得不错的给大家。软件叫 ANYCHAT SDK(以下简称…

Spring Boot 不使用默认的 parent,改用自己的项目的 parent

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到教程。 在初学spring boot时,官方示例中,都是让我们继承一个spring的 spring-boot-starter-parent 这个parent&#xff…

软件工程—让软件包自带commit id

软件开发中软件版本号是一个重要的概念,而对于工程师软件版本号所对应的git(svn)commit id则更重要,嵌入式固件,移动端app开发中,理想的情况下是我拿到一个固件包,或者一个嵌入式设备&#xff0…

ASM 判定一个类,实现了指定接口

为什么80%的码农都做不了架构师?>>> ASM 判定一个类,实现了指定接口 技术支持 ASM 中,ClassReader 类:对已存在的进行解析,并提供获取类信息的方法。 通过 ClassReader ,实现对一个类的解析。 …

流媒体数据代理----Anychat

“用户流媒体数据代理功能”,使用该功能可以实现指定用户将其它用户的流媒体数据(音频、视频)做为自己的数据,转发给请求指定用户相关数据的用户。 应用场景:将指定用户(P)设置为其它用户&…

“javax.servlet.http.HttpServlet“ 错误

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到教程。 我们在进行java web的开发的时候,新建jsp页面,可能会出现The superclass "javax.servlet.http.HttpServlet…

WeakReference带来了什么

转载自:WeakReference带来了什么 很多人说到:java存在内存泄漏。 我不想反驳,因为我也开始慢慢说了,但我知道:内存泄漏和规范编码是两个完全不同的概念,所以我想说:请规范编码 java的“内存泄…

视频聊天室开发详解

互联网的不断发展,各种新技术的兴起,原本做管理软件的我也逐渐转向从事着互联网相关的运营产品的开发。尤其是目前抄得最火热的音视频互动平台技术,今天我先列出最基本开发流程,适用于开发视频会议系统、语音视频聊天系统、远程教…

Android应用开发—ViewPager FragmentPagerAdapter和FragmentStatePagerAdapter下Fragment的生命周期

ViewPager配合不同的PagerAdapter,对应Fragment的生命周期有着不同的表现,了解这个生命周期机制对于开发者选择合适的PagerAdapter实现不同的效果,有着很大的帮助。 FragmentPagerAdapter和FragmentStatePagerAdapter的区别: Fra…

Bash常用快捷键及其作用

Bash常用快捷键及其作用 在 Bash 中有非常多的快捷键,如果可以熟练地使用这些快捷键,可有效地提高我们的工作效率。只是快捷键相对较多,不太好记忆,这就要多加练习和使用。这些快捷键如表 1 所示。 表 1 Bash 常用快捷键快捷键作 …

Putty(菩提)远程连接服务器教程

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到教程。 百度不准直接用其图片,我也懒得去偷,只是记录下原文地址: https://jingyan.baidu.com/article/4543…

V8 中更快的异步函数和 promises

原文作者:Maya Lekova and Benedikt Meurer译者:UC 国际研发 Jothy写在最前:欢迎你来到“UC国际技术”公众号,我们将为大家提供与客户端、服务端、算法、测试、数据、前端等相关的高质量技术文章,不限于原创与翻译。一…

Android应用开发—浅谈MVX模式

MVX模式的文章太多了,这里不会再重述,关键我还不太懂,本文会从自己的一些思考,分析下MVX的分离思想,先占个坑! Android应用架构这篇文章最后讲到: Activity和Fragment变得非常轻量。他们唯一的…

音视频互动平台--P2P通信技术

压缩包可以从这里获得:http://anychat.storage.aliyun.com/AnyChatCoreSDK_Win32(MBCS)_V4.5.zip 下载后,首先将其解压到一个临时文件夹中,然后开始后续工作。 编译后我们获得的是.lib文件。 AnyChat SDK向上层应用开放了内核的P2P连接策略…

pigz 压缩

压缩工具--pigz 压缩: tar cvf - 目录名 | pigz -9 -p 24 > file.tgz pigz:用法-9是压缩比率比较大,-p是指定cpu的核数。 解压: pigz -d file.tgz 这时候是tar包,那么在用 tar -xvf file.tar 解包。 速度比较gz.tar格式的压缩…

exgcd模板

逆元模板P1082 1 #include <cstdio>2 #include <algorithm>3 4 int exgcd(int a, int b, int &x, int &y) {5 if(!b) {6 x 1;7 y 0;8 return a;9 } 10 int g exgcd(b, a % b, x, y); 11 std::swap(x, y); 12 …