关于多级别菜单栏或者权限系统中部门上下级的树形遍历,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
版权声明:本文为博主原创文章,未经博主允许不得转载。
: » MySQL实现树形的遍历、部门递归、菜单级联
原创文章,作者:506227337,如若转载,请注明出处:https://blog.ytso.com/252347.html