mysql树结构数据处理

mysql处理树比较麻烦,不能像oracle那样直接start_with(rootnode)这样,所以只能利用存储过程或者函数来实现比较复杂的逻辑操作了,比如树的等级计算,子节点查询,树的复制等等。

节点的等级计算

需求:已知节点id,需要返回节点的level,也就是层级(root算0级,往下一层算1级这样)

实现思路:

  • 函数实现,传入节点ID,返回节点level
  • 由子节点向上查,是单线,所以一个循环就能搞定
CREATE DEFINER = `root`@`localhost` FUNCTION `getLevel`(`id` bigint)
 RETURNS int(11)
BEGIN
 -- level 待返回的等级变量
 -- pId 用来存储父ID
    declare level bigint;
    declare pId bigint;
    
  -- 变量初始化
    set level = 0;

    select PARENT_DETAIL_ID into pId from hss_setup_trx_detail_b where TRX_DETAIL_ID = id;

    while pId is not null do

    set level = level + 1;

    select PARENT_DETAIL_ID into pId from hss_setup_trx_detail_b where TRX_DETAIL_ID = pId;
    
    end while;

    RETURN level;
END;

树的子节点查询

这个问题其实我自己想了一段时间,也实现了,但是过程比较麻烦,就不贴了。后来参考了前辈们的思路,使用FIND_IN_SET()基本可以完美的解决节点的存储问题。感觉还是sql太菜了,所以实现的思路也一直向java或者c++靠,导致很多问题不知道怎么解决,继续努力学习吧。

需求:废话不多说了,回到正题。需求基本就是,已知树中一点节点id(root或者任意子节点都OK),获取所有子节点的集合。

mysql函数不能递归调用,这是重要前提!

实现思路:

  • 将查询到的节点id存储到集合中
  • 循环实现,获取集合中节点的子节点,再加入集合中
  • 知道子节点为NULL停止

实现此功能需要用到的三个mysql内置函数:

  • concat()连接字符串,mybaits中比较常见
  • group_concat() 将集合并为字符串
  • find_in_set() 类似于like,见图解释

** 贴上官方文档,功能一看demo便知**

FIND_IN_SET
CREATE DEFINER = `root`@`localhost` FUNCTION `getTreeNodes`(`id` bigint)
 RETURNS varchar(1000)
BEGIN
    DECLARE nodes VARCHAR(1000);
    DECLARE tempNodes VARCHAR(1000);
    
  -- nodes 必须初始化,否则null并任意str都返回null
    SET nodes = '$';
    SET tempNodes =cast(id as CHAR);

    WHILE tempNodes is not null DO
    SET nodes = concat(nodes ,',',sTempChd);
    SELECT group_concat(TRX_DETAIL_ID) INTO tempNodes FROM hss_setup_trx_detail_b where FIND_IN_SET(PARENT_DETAIL_ID,tempNodes )>0;

    end while;
    RETURN nodes ;
END;

复制树

基本思路还是按照查询树的思路,但是需要使用到临时表来存储和更新父节点数据。因为游标的特性(单向,不能更新,只能循环一遍),有点类似于java的迭代器。
所以采用临时表才对数中的父ID进行更新,然后插入表中,循环往复这样子。

直接贴代码,逻辑并不复杂,整体类似上述的树的查询。传入的参数ID就是root节点,函数实现的功能就是原表里复制一个tree出来,主键自增,其它两个参数是为了完成项目功能加进去的,不影响整体思路和理解。

说明:

  • temp_tree_detial 就是建立的临时表
  • hss_setup_trx_detail_b 是存储tree的表

思路:首先获取原树的所有节点,然后查询并建立临时表来存储数据。接着,插入root节点,获取root节点的新id,更新临时表里root节点的pId,之后就是重复此过程,知道临时表里面的数据全部插入原表结束。最后返回了新插入数据的节点集合。

CREATE DEFINER = `root`@`localhost` FUNCTION `copyTree`(`id` bigint ,`trxId` bigint, `createBy` bigint)
 RETURNS varchar(1000)
-- 参数: name:id  type:bigint, name:trxId  type:bigint  name:createBy  type:bigint
-- 返回新增的tree节点集合: varchar(1000)
-- 功能: 根据传入的root节点的id,在原表里复制一个新的tree
BEGIN
    -- 用来存储之前tree的id集合
    declare sTemp VARCHAR(1000);
    declare sTempChd VARCHAR(1000);
    
    -- 用来存储新的tree的id集合
    DECLARE sTempNew VARCHAR(1000);
    declare sTempChdNew VARCHAR(1000);
    
    -- 存储上次插入的数据的新id
    declare last_insert_detail_id bigint default 0;

    -- 上次插入数据的原ID
    declare temp_id bigint;

    -- 计数器,存储临时表中剩余的数据,控制循环
    declare count_num bigint;
    
    -- 临时存储插入数据的变量
    declare TEMP_TRX_ID bigint;
    declare TEMP_DETAIL_TRX_TYPE varchar(240);
    declare TEMP_DETAIL_TRX_TYPE_NAME varchar(240);
    declare TEMP_CATEGORY_CODE varchar(30);
    declare TEMP_PARENT_DETAIL_ID bigint;
    declare TEMP_IS_DISPLAY_FLAG varchar(1);
    declare TEMP_CREATED_BY bigint;
    declare TEMP_CREATION_DATE bigint;
    
    -- 初始化变量
    SET sTemp = '$';
    SET sTempNew = '$';
    
    DROP TEMPORARY TABLE IF EXISTS temp_tree_detial;

    CREATE TEMPORARY TABLE temp_tree_detial  SELECT *  FROM hss_setup_trx_detail_b where FIND_IN_SET(TRX_DETAIL_ID,getTreeNodes(id))>0;
    
    -- 拿到root节点的记录,并直接插入原表
    insert into hss_setup_trx_detail_b (TRX_ID,DETAIL_TRX_TYPE,DETAIL_TRX_TYPE_NAME,
        CATEGORY_CODE,PARENT_DETAIL_ID,IS_DISPLAY_FLAG
        ,CREATED_BY,CREATION_DATE)
    select trxId,DETAIL_TRX_TYPE,DETAIL_TRX_TYPE_NAME,
        CATEGORY_CODE,PARENT_DETAIL_ID,IS_DISPLAY_FLAG
        ,createBy,now() from temp_tree_detial where TRX_DETAIL_ID = id;

    -- 获取刚刚插入的数据的id
    SELECT LAST_INSERT_ID() into last_insert_detail_id;

    -- temp_id 用来存储插入前记录的detail_id
    set temp_id = id;

    SET sTempChd =cast(temp_id as CHAR);
    SET sTemp = concat(sTemp,',',sTempChd);

    SET sTempChdNew =cast(last_insert_detail_id as CHAR);
    SET sTempNew = concat(sTempNew,',',sTempChdNew);

    delete from temp_tree_detial where FIND_IN_SET(TRX_DETAIL_ID,sTemp)>0;

    -- 获取已经插入的节点的子节点数量,大于零0进入循环
    select count(1)  into count_num from temp_tree_detial where FIND_IN_SET(PARENT_DETAIL_ID,sTemp)>0;
    
    while count_num > 0  do

        
    -- 根据刚刚插入的节点,更新子节点的pid
    update temp_tree_detial set PARENT_DETAIL_ID = last_insert_detail_id where PARENT_DETAIL_ID = temp_id;

        
    -- 从已经插入了的节点的子节点集合中获取一条数据
    select TRX_DETAIL_ID,TRX_ID,DETAIL_TRX_TYPE,DETAIL_TRX_TYPE_NAME, CATEGORY_CODE,PARENT_DETAIL_ID,IS_DISPLAY_FLAG ,CREATED_BY,CREATION_DATE
    into temp_id,TEMP_TRX_ID,TEMP_DETAIL_TRX_TYPE,TEMP_DETAIL_TRX_TYPE_NAME,TEMP_CATEGORY_CODE,TEMP_PARENT_DETAIL_ID,TEMP_IS_DISPLAY_FLAG,TEMP_CREATED_BY,TEMP_CREATION_DATE
    from temp_tree_detial where FIND_IN_SET(PARENT_DETAIL_ID,sTempNew)>0 limit 1;
    

    -- 插入刚刚获取到的数据
    insert into hss_setup_trx_detail_b (TRX_ID,DETAIL_TRX_TYPE,DETAIL_TRX_TYPE_NAME, CATEGORY_CODE,PARENT_DETAIL_ID,IS_DISPLAY_FLAG ,CREATED_BY,CREATION_DATE)
    values (trxId,TEMP_DETAIL_TRX_TYPE,TEMP_DETAIL_TRX_TYPE_NAME,TEMP_CATEGORY_CODE,TEMP_PARENT_DETAIL_ID,TEMP_IS_DISPLAY_FLAG,createBy,now());

    SELECT LAST_INSERT_ID() into last_insert_detail_id;

    -- 更新数据集
    SET sTempChdNew =cast(last_insert_detail_id as CHAR);
    SET sTempNew = concat(sTempNew,',',sTempChdNew);
    SET sTempChd =cast(temp_id as CHAR);
    SET sTemp = concat(sTemp,',',sTempChd);

    -- 从临时表里面删除已经插入原表的记录
    delete from temp_tree_detial where FIND_IN_SET(TRX_DETAIL_ID,sTemp)>0;

    select count(1)  into count_num from temp_tree_detial;

    end while;

    RETURN sTempNew;
END

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,384评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,845评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,148评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,640评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,731评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,712评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,703评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,473评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,915评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,227评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,384评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,063评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,706评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,302评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,531评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,321评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,248评论 2 352

推荐阅读更多精彩内容