视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
[MySQL]实现树形的遍历(关于多级菜单栏以及多级上下部门的查询问题)_MySQL
2020-11-09 19:42:45 责编:小采
文档


前言:
关于多级别菜单栏或者权限系统中部门上下级的树形遍历,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://jan.kneschke.de/projects/mysql/sp/sp_tree.sql
http://blog.csdn.net/ylqmf/article/details/5172901

下载本文
显示全文
专题