[MySQL] 实现树形的遍历(关于多级菜单栏以及多级上下部门的查询问题)
前言:
关于多级别菜单栏或者权限系统中部门上下级的树形遍历,oracle中有connect by来实现,MySQL没有这样的便捷途径,所以MySQL遍历数据表是我们经常会遇到的头痛问题,下面通过存储过程来实现。
如果在执行SQL语句的时候报错,删除END后的¥符号即可
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
[MySQL] 实现树形的遍历(关于多级菜单栏以及多级上下部门的查询问题)的更多相关文章
- mysql 实现树形的遍历
前言:关于多级别菜单栏或者权限系统中部门上下级的树形遍历,oracle中有connect by来实现,mysql没有这样的便捷途径,所以MySQL遍历数据表是我们经常会遇到的头痛问题,下面通过存储过程 ...
- mysql存储过程之游标遍历数据表
原文:mysql存储过程之游标遍历数据表 今天写一个mysql存储过程,根据自己的需求要遍历一个数据表,因为对存储过程用的不多,语法不甚熟悉,加之存储过程没有调试环境,花了不少时间才慢慢弄好,故留个痕 ...
- sqlite3树形结构遍历效率对照測试
sqlite3树形结构遍历效率对照測试 一.缘起 项目数据结构:本人从事安防行业,视频监控领域.项目中会遇到监控点位的组织机构划分.暂时划分的巡逻点位等.这些相机点位.连同组织机构,它们在逻辑关系上构 ...
- MySQL 使用profile分析慢sql,group left join效率高于子查询
MySQL 使用profile分析慢sql,group left join效率高于子查询 http://blog.csdn.net/mchdba/article/details/54380221 -- ...
- Atitit.mysql oracle with as模式临时表模式 CTE 语句的使用,减少子查询的结构性 mssql sql server..
Atitit.mysql oracle with as模式临时表模式 CTE 语句的使用,减少子查询的结构性 mssql sql server.. 1. with ... as (...) 在mys ...
- 使用Oracle数据库实现树形结构表的子-父级迭代(递归)查询和删除,通过级联菜单简单举例
前言: 我们在开发中,常常遇到单表的子-父id级联的表结构,在树形的深度不确定的情况下,一次查询出某个树形结构下的所有具有子-父级关系的数据变得十分困难. 这时,我们使用oracle提供的CONNEC ...
- 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节点, ...
随机推荐
- C++ 类模板二(类模版与友元函数)
//类模版与友元函数 #include<iostream> using namespace std; template<typename T> class Complex{ p ...
- JavaScript如何实现计时功能。
JavaScript如何实现计时功能. 解答:<script> window.setTimeout( “window.location = ‘http://www.csdn.net ‘; ...
- Spring中HttpInvoker远程方法调用总结
Spring为各种远程訪问技术的集成提供了工具类.Spring远程支持是由普通(Spring)POJO实现的,这使得开发具有远程訪问功能的服务变得相当easy. 眼下,Spring支持四种远程技术: ...
- 理解WCF中的Contracts
WCF中的Contracts WCF通过Contract来说明服务和操作,一般包含五种类型的Contract:ServiceContract,OperationContract,FaultContra ...
- WPFLoading遮层罩
一直想实现这么个功能来着,所以去网上搜了资料,复杂的看不懂,后来挑了一个最简单的,复用了这位大神的很多代码(大神看到了别打脸).这位大神是用UserControl,使用时则是调用用户控件中的方法.之前 ...
- shell面试题总结
1) 如何向脚本传递参数 ? ./script argument 例子: 显示文件名称脚本 ./show.sh file1.txt cat show.sh #!/bin/bash echo $1 (L ...
- iOS捕获异常,常用的异常处理方法
本文转载至 http://www.cocoachina.com/ios/20141229/10787.html 前言:在开发APP时,我们通常都会需要捕获异常,防止应用程序突然的崩溃,防止给予用户不友 ...
- Android去掉标题的方法
我们写程序的时候经常要全屏显示或者不显示标题.比如我们做地图导航的时候就不要标题了,下面介绍三种方法来实现Android去掉标题. 第一种:也一般入门的时候经常使用的一种方法 在setContentV ...
- jq 选择器基础及拓展
jquery 用的很多,所以jq的选择器就很受欢迎,但是用的过程中有一些小问题,如果不点透就永远不知道. 1:ID选择器:$("#ID"); 得到一个指定对应,并且只能得到一个对象 ...
- java的list去重
Set<EmployeeInfoDTO> empSet = new HashSet<EmployeeInfoDTO>(empListAll);List<EmployeeI ...