MySQL实现树形的遍历、部门递归、菜单级联

SQL herman 3893浏览 0评论
公告:“业余草”微信公众号提供免费CSDN下载服务(只下Java资源),关注业余草微信公众号,添加作者微信:xttblog,发送下载链接帮助你免费下载!
本博客日IP超过1800,PV 2600 左右,急需赞助商。
极客时间所有课程通过我的二维码购买后返现24元微信红包,请加博主新的微信号:xttblog,之前的微信号好友位已满,备注:返现
所有面试题(java、前端、数据库、springboot等)一网打尽,请关注文末小程序
视频教程免费领

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

在开始之前我们先建立测试表和数据,如下:

DROP TABLE IF EXISTS dept;   
CREATE TABLE dept (   
  id INT(11) NOT NULL AUTO_INCREMENT,     
  dept_name VARCHAR(200) DEFAULT NULL,   
  parent_id INT(11) DEFAULT NULL,   
  PRIMARY KEY (id)   
) ENGINE=INNODB DEFAULT CHARSET=utf8;   
INSERT INTO dept(id,dept_name,parent_id)    
VALUES (13,'研发部门',-1),   
       (14,'财务部',-1),   
       (15,'市场部',-1),   
       (16,'研发一部',13),   
       (17,'上海财务组',14),   
       (18,'华东市场业务组',17);  

接下来我们可以利用临时表和递归过程实现树的遍历(注意:mysql的UDF不能递归调用)

从某节点向下遍历子节点,递归生成临时表数据

DELIMITER $$     
DROP PROCEDURE IF EXISTS xttblog.pro_cre_childlist$$   
CREATE PROCEDURE xttblog.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 dept 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$$
DELIMITER $$

从某节点向上追溯根节点,递归生成临时表数据

DELIMITER $$
DROP PROCEDURE IF EXISTS xttblog.pro_cre_parentlist$$   
CREATE PROCEDURE xttblog.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 dept 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$$
DELIMITER $$

实现类似Oracle SYS_CONNECT_BY_PATH的功能,递归过程输出某节点id路径

DELIMITER $$
USE db$$
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 dept 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 $$

递归过程输出某节点name路径

DELIMITER $$
USE db$$
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.dept_name,delimit,pathstr)   
        FROM dept 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 $$

调用函数输出id路径

DELIMITER $$ 
DROP FUNCTION IF EXISTS db.fn_tree_path$$   
CREATE FUNCTION db.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$$
DELIMITER $$

调用函数输出name路径

DELIMITER $$
DROP FUNCTION IF EXISTS db.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 $$ 

调用过程输出子节点

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 d.id,CONCAT(SPACE(tmpLst.depth*2),'--',d.dept_name) NAME,d.parent_id,tmpLst.depth,fn_tree_path(d.id,'/') path,fn_tree_pathname(d.id,'/') pathname   
      FROM tmpLst,dept d WHERE tmpLst.id=d.id ORDER BY tmpLst.sno;   
END$$
DELIMITER $$

调用过程输出父节点

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 d.id,CONCAT(SPACE(tmpLst.depth*2),'--',d.dept_name) NAME,d.parent_id,tmpLst.depth,fn_tree_path(d.id,'/') path,fn_tree_pathname(d.id,'/') pathname   
      FROM tmpLst,dept d WHERE tmpLst.id=d.id ORDER BY tmpLst.sno;   
END$$ 
DELIMITER $$

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

版权声明:本文为博主原创文章,未经博主允许不得转载。

业余草公众号

最后,欢迎关注我的个人微信公众号:业余草(yyucao)!可加QQ1群:135430763(2000人群已满),QQ2群:454796847(已满),QQ3群:187424846(已满)。QQ群进群密码:xttblog,想加微信群的朋友,之前的微信号好友已满,请加博主新的微信号:xttblog,备注:“xttblog”,添加博主微信拉你进群。备注错误不会同意好友申请。再次感谢您的关注!后续有精彩内容会第一时间发给您!原创文章投稿请发送至532009913@qq.com邮箱。商务合作可添加助理微信进行沟通!

本文原文出处:业余草: » MySQL实现树形的遍历、部门递归、菜单级联