侯体宗的博客
  • 首页
  • Hyperf版
  • beego仿版
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 中国象棋ai
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

在Mysql数据库里通过存储过程实现树形的遍历

数据库  /  管理员 发布于 8年前   175

关于多级别菜单栏或者权限系统中部门上下级的树形遍历,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_childlistDROP PROCEDURE IF EXISTS csdn.pro_cre_childlistCREATE PROCEDURE csdn.pro_cre_childlist(IN rootId INT,IN nDepth INT)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; 

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

-- pro_cre_parentlistDROP PROCEDURE IF EXISTS csdn.pro_cre_parentlistCREATE 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; 

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

-- pro_cre_pathlistUSE csdnDROP PROCEDURE IF EXISTS pro_cre_pathlistCREATE 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; DELIMITER ;

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

-- pro_cre_pnlistUSE csdnDROP PROCEDURE IF EXISTS pro_cre_pnlistCREATE 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; DELIMITER ;

2.5,调用函数输出id路径

-- fn_tree_pathDELIMITERDROP FUNCTION IF EXISTS csdn.fn_tree_pathCREATE 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路径 DELIMITERDROP FUNCTION IF EXISTS csdn.fn_tree_pathnameCREATE 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; ENDDELIMITER ;

2.7,调用过程输出子节点

-- pro_show_childLst DELIMITER-- 调用过程输出子节点 DROP PROCEDURE IF EXISTS pro_show_childLstCREATE 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_parentLstDELIMITER-- 调用过程输出父节点 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,显示“帮忙”节点的父节点

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>

以上所述是小编给大家介绍的在Mysql数据库里通过存储过程实现树形的遍历,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对站的支持!


  • 上一条:
    使用JDBC在MySQL数据库中如何快速批量插入数据
    下一条:
    Mysql数据库中子查询的使用
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • 分库分表的目的、优缺点及具体实现方式介绍(0个评论)
    • DevDB - 在 VS 代码中直接访问数据库(0个评论)
    • 在ubuntu系统中实现mysql数据存储目录迁移流程步骤(0个评论)
    • 在mysql中使用存储过程批量新增测试数据流程步骤(0个评论)
    • php+mysql数据库批量根据条件快速更新、连表更新sql实现(0个评论)
    • 近期文章
    • 智能合约Solidity学习CryptoZombie第三课:组建僵尸军队(高级Solidity理论)(0个评论)
    • 智能合约Solidity学习CryptoZombie第二课:让你的僵尸猎食(0个评论)
    • 智能合约Solidity学习CryptoZombie第一课:生成一只你的僵尸(0个评论)
    • 在go中实现一个常用的先进先出的缓存淘汰算法示例代码(0个评论)
    • 在go+gin中使用"github.com/skip2/go-qrcode"实现url转二维码功能(0个评论)
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(0个评论)
    • gmail发邮件报错:534 5.7.9 Application-specific password required...解决方案(0个评论)
    • 欧盟关于强迫劳动的规定的官方举报渠道及官方举报网站(0个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf文件功能(0个评论)
    • 近期评论
    • 122 在

      学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..
    • 123 在

      Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..
    • 原梓番博客 在

      在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..
    • 博主 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..
    • 1111 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
    • 2017-06
    • 2017-08
    • 2017-09
    • 2017-10
    • 2017-11
    • 2018-01
    • 2018-05
    • 2018-10
    • 2018-11
    • 2020-02
    • 2020-03
    • 2020-04
    • 2020-05
    • 2020-06
    • 2020-07
    • 2020-08
    • 2020-09
    • 2021-02
    • 2021-04
    • 2021-07
    • 2021-08
    • 2021-11
    • 2021-12
    • 2022-02
    • 2022-03
    • 2022-05
    • 2022-06
    • 2022-07
    • 2022-08
    • 2022-09
    • 2022-10
    • 2022-11
    • 2022-12
    • 2023-01
    • 2023-03
    • 2023-04
    • 2023-05
    • 2023-07
    • 2023-08
    • 2023-10
    • 2023-11
    • 2023-12
    • 2024-01
    • 2024-03
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客