MySQL 树形结构 根据指定节点 获取其所有叶子节点

时间:2022-05-20 23:47:29

背景说明

需求:MySQL树形结构, 根据指定的节点,获取其下属的所有叶子节点。

叶子节点:如果一个节点下不再有子节点,则为叶子节点。

问题分析

1、可以使用类似Java这种面向对象的语言,对节点集合进行逻辑处理,获取叶子节点。

2、直接自定义MySQL函数 getLeafNodeList,通过两层while循环,实现对指定节点的所有叶子节点进行查询。

功能实现

1、创建数据表

1)表结构截图如下(此处简单建一张表 t_tree,id主键自增,uuid表示本节点,parent_uuid表示父节点):

MySQL 树形结构 根据指定节点 获取其所有叶子节点

2)建表语句如下:

/*
Navicat Premium Data Transfer Source Server : localhost
Source Server Type : MySQL
Source Server Version : 50724
Source Host : localhost:3306
Source Schema : test_db Target Server Type : MySQL
Target Server Version : 50724
File Encoding : 65001 Date: 07/05/2019 21:04:57
*/ SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0; -- ----------------------------
-- Table structure for t_tree
-- ----------------------------
DROP TABLE IF EXISTS `t_tree`;
CREATE TABLE `t_tree` (
`id` int(20) NOT NULL AUTO_INCREMENT,
`uuid` int(20) NULL DEFAULT NULL,
`parent_uuid` int(20) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 15 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ----------------------------
-- Records of t_tree
-- ----------------------------
INSERT INTO `t_tree` VALUES (1, 1, 0);
INSERT INTO `t_tree` VALUES (2, 2, 0);
INSERT INTO `t_tree` VALUES (3, 3, 0);
INSERT INTO `t_tree` VALUES (4, 11, 1);
INSERT INTO `t_tree` VALUES (5, 12, 1);
INSERT INTO `t_tree` VALUES (6, 21, 2);
INSERT INTO `t_tree` VALUES (7, 22, 2);
INSERT INTO `t_tree` VALUES (8, 211, 21);
INSERT INTO `t_tree` VALUES (9, 221, 22);
INSERT INTO `t_tree` VALUES (10, 222, 22);
INSERT INTO `t_tree` VALUES (11, 223, 22);
INSERT INTO `t_tree` VALUES (12, 2231, 223);
INSERT INTO `t_tree` VALUES (13, 2232, 223);
INSERT INTO `t_tree` VALUES (14, 0, ); SET FOREIGN_KEY_CHECKS = 1;

3)表数据结构如下:

MySQL 树形结构 根据指定节点 获取其所有叶子节点

4)树形结构如下图:

MySQL 树形结构 根据指定节点 获取其所有叶子节点

2、编写查询叶子节点函数 getLeafNodeList,如下:

CREATE DEFINER=`root`@`localhost` FUNCTION `getLeafNodeList`(`nodeId` int) RETURNS varchar(1000) CHARSET utf8
BEGIN
DECLARE leafNodeList VARCHAR(1000); # 返回叶子节点结果集
DECLARE tempChild VARCHAR(1000); # 临时存放子节点
DECLARE count int; # 计算节点下是否有节点,count = 0 表示为叶子节点
DECLARE tempLeaf VARCHAR(1000); # 临时存放可能的叶子节点
DECLARE leafNode VARCHAR(1000); # 存放叶子节点 SET leafNodeList = '';
SET tempChild = CAST(nodeId as CHAR); # 将int类型转换为String类型 WHILE tempChild is not null DO # 外层循环,用于查询节点下所有的子节点
SET tempLeaf = tempChild; # 临时存放节点,用于内层循环判定是否为叶子节点,避免影响外层循环使用 tempChild
WHILE LENGTH(tempLeaf) > 0 DO # 内层循环,用于判断外层查询到的子节点是否为叶子节点
SET leafNode = SUBSTRING_INDEX(tempLeaf, ',', 1); # 假定逗号分隔的第一个为叶子节点
select count(uuid) INTO count from t_tree where parent_uuid = leafNode; # 查询该节点下是否有子节点
IF count = 0 THEN
SET leafNodeList = CONCAT(leafNodeList, ',', leafNode); # 如果该节点下没有子节点,则认为是叶子节点,存入到返回结果中
END IF;
SET tempLeaf = SUBSTRING(tempLeaf, LENGTH(leafNode) + 2); # 将第一个节点截取掉,继续识别剩余的节点
END WHILE; SELECT GROUP_CONCAT(uuid) INTO tempChild FROM t_tree where FIND_IN_SET(parent_uuid, tempChild) > 0; # 查询节点下所有子节点
END WHILE; RETURN SUBSTRING(leafNodeList, 2); # 将返回结果进行处理,截取掉结果集前面的逗号
END

其中,用到了很多MySQL的系统函数,如:CAST,SUBSTRING,SUBSTRING_INDEX,CONCAT,GROUP_CONCAT,FIND_IN_SET。

 3、调用函数

select getLeafNodeList(1) as leafNodeList;

0)查询节点0 的叶子节点:从树形图可以看到,应该是 3,11,12,211,221,222,2231,2232

MySQL 树形结构 根据指定节点 获取其所有叶子节点

1)查询节点1 的叶子节点:从树形图可以看到,应该是 11,12

MySQL 树形结构 根据指定节点 获取其所有叶子节点

2)查询节点2 的叶子节点:从树形图可以看到,应该是 211,221,222,2231,2232

MySQL 树形结构 根据指定节点 获取其所有叶子节点

3)查询节点3 的叶子节点:从树形图可以看到,应该是 3

MySQL 树形结构 根据指定节点 获取其所有叶子节点

问题总结

该问题核心点内层循环查找叶子节点,按照上面的表数据和截图,阅读SQL函数,很好理解。

希望能帮到需要帮助的同行,谢谢。

 PS:

1)如果需要 根据指定的节点,获取其下属的所有子节点(包含路径上的所有枝干节点和叶子节点)

请参考本人的另一篇博文:https://www.cnblogs.com/miracle-luna/p/10828592.html

2)如果需要 根据指定节点,获取其所有父节点序列
请参考本人的另一篇博文:https://www.cnblogs.com/miracle-luna/p/10878224.html

3)如果需要 根据指定节点,获取其所在全路径节点序列

请参考本人的另一篇博文:https://www.cnblogs.com/miracle-luna/p/10878366.html