mysql 实现树形的遍历
前言:
关于多级别菜单栏或者权限系统中部门上下级的树形遍历,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 实现树形的遍历的更多相关文章
- [MySQL] 实现树形的遍历(关于多级菜单栏以及多级上下部门的查询问题)
前言: 关于多级别菜单栏或者权限系统中部门上下级的树形遍历,oracle中有connect by来实现,MySQL没有这样的便捷途径,所以MySQL遍历数据表是我们经常会遇到的头痛问题 ...
- mysql存储过程之游标遍历数据表
原文:mysql存储过程之游标遍历数据表 今天写一个mysql存储过程,根据自己的需求要遍历一个数据表,因为对存储过程用的不多,语法不甚熟悉,加之存储过程没有调试环境,花了不少时间才慢慢弄好,故留个痕 ...
- sqlite3树形结构遍历效率对照測试
sqlite3树形结构遍历效率对照測试 一.缘起 项目数据结构:本人从事安防行业,视频监控领域.项目中会遇到监控点位的组织机构划分.暂时划分的巡逻点位等.这些相机点位.连同组织机构,它们在逻辑关系上构 ...
- MySql/Oracle树形结构查询
Oracle树形结构递归查询 在Oracle中,对于树形查询可以使用start with ... connect by select * from treeTable start with id='1 ...
- Mysql实现树形递归查询
最近在做项目迁移,Oracle版本的迁到Mysql版本,遇到有些oracle的函数,mysql并没有,所以就只好想自定义函数或者找到替换函数的方法进行改造. Oracle递归查询 oracle实现递归 ...
- 如何使用mysql存储树形关系
最近遇到业务的一个类似文件系统的存储需求,对于如何在mysql中存储一颗树进行了一些讨论,分享一下,看看有没有更优的解决方案. 一.现有情况 首先,先假设有这么一颗树,一共9个节点,1是root节点, ...
- mysql处理百万数据遍历速度提升(遍历图片名字是否存在)
CREATE DEFINER=`root`@`localhost` FUNCTION `fun_wcmappendix02`(image_name VARCHAR(50)) RETURNS int(1 ...
- MySQL 树节点递归遍历所以子节点
DELIMITER $$ DROP FUNCTION IF EXISTS `getChildList`$$ CREATE FUNCTION `getChildList`(rootId INT) RET ...
- MySQL存储过程-->通过游标遍历和异常处理迁移数据到历史表
-- 大表数据迁移,每天凌晨1点到5点执行,执行间隔时间10分钟,迁移旧数据到历史表. DELIMITER $$ USE `dbx`$$ DROP PROCEDURE IF EXISTS `pro_x ...
随机推荐
- Genymotion安卓模拟器和VirtualBox虚拟机安装、配置、测试
Genymotion安卓模拟器和VirtualBox虚拟机安装.配置.测试(win7_64bit) 目录 1.概述 2.本文用到的工具 3.VirtualBox虚拟机安装 4.Genymotion安卓 ...
- apk公钥私钥用法
每个密钥都包含两个文件:一个是扩展名为 .x509.pem 的证书,另一个是扩展名为 .pk8 的私钥.私钥需要加以保密,并用于对 apk 包进行签名.密钥本身也可能受密码保护.相比之下,证书只包含公 ...
- matlab练习程序(点集配准的SVD法)
上一篇博客中我们使用了四元数法计算ICP. 本篇我们使用SVD计算ICP. 下面是<视觉slam十四讲>中的计算方法: 计算步骤如下: 我们看到,只要求出了两组点之间的旋转,平移是非常容易 ...
- 【PHPStorm使用手册】如何设置字体大小?
方案一:鼠标滚轮设置 打开窗口 file -> settings -> Editor -> General, 勾上选项“Change font size(Zoom) with Ctr ...
- 洗礼灵魂,修炼python(76)--全栈项目实战篇(4)—— 购物车系统
要求: 1.基本符合日常购物车的要求(根据你的想法开放性提升功能) 2.展示商品信息,并且可随时上新商品 3.用户购买每一样商品时都对所剩的钱做一次对比,如果够则提示“已购买”,如果不够提示“余额不足 ...
- ASP.NET MVC从视图传参到控制器的几种形式
1. 传递数组 $(function () { var value = ["C#", "JAVA", "PHP"]; $("inp ...
- git 使用命令删除远程分支和本地分支
删除远程分支命令: git push origin :<远程分支名称> git push origin --delete <远程分支名称> 删除本地分支: git bran ...
- C++实现程序单实例运行的两种方式
简介 在我们编写程序的时候,经常会注意到的一个问题就是如何能够让程序只运行一个实例,确保不会让同一个程序多次运行,从而产生诸多相同进程,给我们的带来不便呢?那么常用的有以下四种方法,第一种方法是通过扫 ...
- Windows Server2008 R2安装wampserver缺少api-ms-win-crt-runtime-l1-1-0.dll解决方案
安装wampserver经常会遇到缺少各种dll文件的问题,可以在安装之前先安装一下微软运行库合集,但此时仍有可能缺少api-ms-win-crt-runtime-l1-1-0.dll文件,那么可以尝 ...
- 基于python的Selenium使用小结
之前介绍过基于Unittest和TestNG自动化测试框架,然而基于Web端的测试的基础框架是需要Selenium做主要支撑的,这里边给大家介绍下Web测试核心之基于Python的Selenium 一 ...