MySQL 樹形結構 根據指定節點 獲取其所有葉子節點


背景說明

需求:MySQL樹形結構, 根據指定的節點,獲取其下屬的所有葉子節點。

葉子節點:如果一個節點下不再有子節點,則為葉子節點。

 

問題分析

1、可以使用類似Java這種面向對象的語言,對節點集合進行邏輯處理,獲取葉子節點。

2、直接自定義MySQL函數 getLeafNodeList,通過兩層while循環,實現對指定節點的所有葉子節點進行查詢。

 

功能實現

1、創建數據表

1)表結構截圖如下(此處簡單建一張表 t_tree,id主鍵自增,uuid表示本節點,parent_uuid表示父節點):

 

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)表數據結構如下:

 

4)樹形結構如下圖:

 

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

 

1)查詢節點1 的葉子節點:從樹形圖可以看到,應該是 11,12

 

2)查詢節點2 的葉子節點:從樹形圖可以看到,應該是 211,221,222,2231,2232

 

3)查詢節點3 的葉子節點:從樹形圖可以看到,應該是 3

 

問題總結

該問題核心點內層循環查找葉子節點,按照上面的表數據和截圖,閱讀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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM