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

1、建立测试表和数据:

DROP TABLE IF EXISTS test.channel;
CREATE TABLE test.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);

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

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

-- 递归输出某节点id路径
DELIMITER //
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;
-- 下面这行表示若没有数据返回,程序继续,并将变量done设为1
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
-- mysql中可以利用系统参数 max_sp_recursion_depth 来控制递归调用的层数上限。
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 ;

测试:

SET @str='';
CALL pro_cre_pathlist(16,'/',@str);
SELECT @str;

测试结果:

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

-- 递归输出某节点name路径
DELIMITER //
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;
-- 下面这行表示若没有数据返回,程序继续,并将变量done设为1
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
-- mysql中可以利用系统参数 max_sp_recursion_depth 来控制递归调用的层数上限。
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 ;

测试:

SET @str='';
CALL pro_cre_pnlist(16,'/',@str);
SELECT @str;

测试结果:

2.3、调用函数输出id路径

-- 调用函数输出id路径
DELIMITER //
DROP FUNCTION IF EXISTS fn_tree_path;
CREATE FUNCTION 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 ;

测试:

SELECT fn_tree_path(16,'/') AS id;

测试结果:

2.4、调用函数输出name路径

-- 调用函数输出name路径
DELIMITER //
DROP FUNCTION IF EXISTS fn_tree_pathname;
CREATE FUNCTION 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 ;

测试:

SELECT fn_tree_pathname(16,'/') AS name;

测试结果:

2.5、调用过程输出子节点

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

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

DELIMITER //
DROP PROCEDURE IF EXISTS pro_cre_childlist;
CREATE PROCEDURE 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 tmpList 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 ;

2.7、调用过程输出父节点

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

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

DELIMITER //
DROP PROCEDURE IF EXISTS pro_cre_parentlist;
CREATE PROCEDURE 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 tmpList 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 ;

3、开始测试

3.1、从根节点开始显示,显示子节点集合:

CALL pro_show_childlist(-1);

测试结果:

3.2、显示首页下面的子节点

CALL pro_show_childlist(13);

测试结果:

3.3、显示TV580下面的所有子节点

CALL pro_show_childlist(14);

测试结果:

3.4、“帮忙”节点有一个子节点,显示出来:

CALL pro_show_childlist(17);

测试结果:

3.5、“栏目简介”没有子节点,所以只显示最终节点:

3.6、显示“首页”的父节点

CALL pro_show_parentlist(13);

测试结果:

3.7、显示“TV580”的父节点,parent_id为-1

CALL pro_show_parentlist(14);

测试结果:

3.8、显示“帮忙”节点的父节点

CALL pro_show_parentlist(17);

测试结果:

3.9、显示最低层节点“栏目简介”的父节点

CALL pro_show_parentlist(18);

测试结果:

mysql 实现树形的遍历的更多相关文章

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

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

  2. mysql存储过程之游标遍历数据表

    原文:mysql存储过程之游标遍历数据表 今天写一个mysql存储过程,根据自己的需求要遍历一个数据表,因为对存储过程用的不多,语法不甚熟悉,加之存储过程没有调试环境,花了不少时间才慢慢弄好,故留个痕 ...

  3. sqlite3树形结构遍历效率对照測试

    sqlite3树形结构遍历效率对照測试 一.缘起 项目数据结构:本人从事安防行业,视频监控领域.项目中会遇到监控点位的组织机构划分.暂时划分的巡逻点位等.这些相机点位.连同组织机构,它们在逻辑关系上构 ...

  4. MySql/Oracle树形结构查询

    Oracle树形结构递归查询 在Oracle中,对于树形查询可以使用start with ... connect by select * from treeTable start with id='1 ...

  5. Mysql实现树形递归查询

    最近在做项目迁移,Oracle版本的迁到Mysql版本,遇到有些oracle的函数,mysql并没有,所以就只好想自定义函数或者找到替换函数的方法进行改造. Oracle递归查询 oracle实现递归 ...

  6. 如何使用mysql存储树形关系

    最近遇到业务的一个类似文件系统的存储需求,对于如何在mysql中存储一颗树进行了一些讨论,分享一下,看看有没有更优的解决方案. 一.现有情况 首先,先假设有这么一颗树,一共9个节点,1是root节点, ...

  7. mysql处理百万数据遍历速度提升(遍历图片名字是否存在)

    CREATE DEFINER=`root`@`localhost` FUNCTION `fun_wcmappendix02`(image_name VARCHAR(50)) RETURNS int(1 ...

  8. MySQL 树节点递归遍历所以子节点

    DELIMITER $$ DROP FUNCTION IF EXISTS `getChildList`$$ CREATE FUNCTION `getChildList`(rootId INT) RET ...

  9. MySQL存储过程-->通过游标遍历和异常处理迁移数据到历史表

    -- 大表数据迁移,每天凌晨1点到5点执行,执行间隔时间10分钟,迁移旧数据到历史表. DELIMITER $$ USE `dbx`$$ DROP PROCEDURE IF EXISTS `pro_x ...

随机推荐

  1. Android和H5进行数据交互,Android获取H5Input框中的内容

    项目中嵌入了H5 页面,这个时候就需要拿到H5 input中的内容进行数据传递,先看实现的效果图

  2. 纯中文C++代码,可运行

    #include <stdio.h>#include <tchar.h> #define 如果 if#define 打印 printf#define 返回 return#def ...

  3. vue自动完成搜索功能的数据请求处理

    在现在的互联网世界里,自动完成的搜索功能是一个很常见的功能.比如百度.搜狗.360搜索 ... 功能描述一下大概是这个样子的:有一个搜索框,用户在里面输入要查询的条件,系统会“智能”判断用户输完了,然 ...

  4. C# 代码中调用 Javascript 代码段以提高应用程序的配置灵活性(使用 Javascript .NET 与 Jint)

    一般来说,我们需要在开发应用软件的配置文件中,添加一些参数,用于后续用户根据实际情况,自行调整. 配置参数,可以放在配置文件中.环境变量中.或数据库表中(如果使用了数据库的话).通常,配置数据,以 k ...

  5. java后台打开浏览器代码

    import java.awt.Desktop; import java.io.IOException; import java.net.URI; import java.net.URISyntaxE ...

  6. Uvision4创建工程

    创建工程 创建好工程会出现如下的界面 创建文件( .asm 或 .c ) 选择菜单栏中的File->New- 按 Ctrl+S 保存文件并填写文件名 添加到之前创建的工程中 选择Target1- ...

  7. SQL SERVER 查询哪些存储使用了该表

    SELECT obj.Name 存储过程名, sc.TEXT 存储过程内容 FROM syscomments sc INNER JOIN sysobjects obj ON sc.Id = obj.I ...

  8. 光盘yum源搭建

    先修改基本的yum原源,使其不生效 cd /etc/yum.repos.d/ mv CentOS-Base.repo CentOS-Base.repo.bak 在修改媒介yum源使其生效 检验是否生效 ...

  9. ArcGIS Server10.2 集群部署注意事项

    不接触Server很久了,最近一个省级项目需要提交一个部署方案,由于是省级系统,数据.服务数量都较大,需要考虑采用Server集群的方式来实现.在网上搜罗了以下Server集群的资料,按照步骤一步步来 ...

  10. c/c++柔性数组成员

    柔性数组成员 定义和声明分离 #include <stdio.h> //只是告诉编译器,当编译到使用到这个函数的的代码时,虽然还没有找到函数定义的实体,但是也让它编译不出错误. exter ...