通常树形结构的存储,是在子节点上存储父节点的编号来确定各节点的父子关系,例如这样的组织结构:
(资料图)
与之对应的表数据(department):
部门表结构(department)
id部门编号name部门名称level所在树层级parent_id上级部门编号
问题来了
这样的方式很不错,可以很直观的体现各个节点之间的关系,通常可以满足大多数需求。但是当业务需求变得多了,数据量庞大了,这样的方式就不再适合用于生产。
例如:PM加了以下需求:
查出指定部门下所有子孙部门查询子孙部门总数判断节点是否叶子节点查出所有子孙部门
使用指定部门编号,一层一层使用递归往下查,可能是多数人会想到的方法。尽管在mysql8.0支持了 cte(公共表表达式),递归效率比传统递归方式有明显提升,但是查询效率仍会随着部门树层级深度的提高而变差。
另外一种方法,一次性查出所有数据,放入内存中处理(数据量少时,可以选用。数据量多,不怕挨打的人也可以选这种)~
查询子孙部门总数
递归查询每一层的数量,最后相加。
判断是否叶子节点
方法1:可以加字段isLeaf的方式,来表示这个节点是否是叶子节点。
方法2:直接通过查询parent_id=当前id的count是否大于0,大于0表示不是叶子节点,等于0表示为叶子节点。
在日常中,可能会经常使用上述类似方法去解决类似的问题,但我觉得这样的方法在效率上不是最优解。于是乎开始查找更好的方案去解决这些问题。
| 要不试试这个方法?
直到后面查到国外一博客中,见到了所谓的《改进后的先序树遍历》文章(天哪,竟然是一篇2003年发表的文章)~
他具体是怎么做的呢?
还是回到刚刚的组织架构
我们从根节点开始,给董事长左值设为1,下级部门总经理左值设为2,以此类推地沿着边缘开始遍历,给每个节点加上左值,遇到叶子节点处给节点加上右值,再继续向上沿着边缘继续遍历,遍历结束回到根节点右侧,你将得到类似这样的结构。
遍历完后每一个节点都有与之对应的左右值。这个时候可以去除parent_id字段,添加lft,rgt,来存储左右值。
数据和结构准备完毕,我们来试试操作解决上面的需求~
查出所有子孙部门
根据当前表结构的规律,可以发现,要想查出所有子孙部门,只要查左值在 被查寻部门的左\右数之间的节点,查出来都是他的子节点。例如:查询行政总监的所有子部门,行政总监的左右数是9和18,因此只需要用9和18做lft字段的between查询,查询出的结果就是【被查部门本身数据和所有子孙部门】;
SET@lft:=9;SET@rgt:=18;SELECT*FROMdepartmentWHERElftBETWEEN@lftAND@rgtORDERBYlftASC;/*例子中用BETWEEN将被查部门本身也查了出来。实际中可以用大于小于*/完美~
查询子孙部门总数
到这里可能会说,需求1都解决了,查总数自然也就解决了,直接上select count就可以了,确实没有错,但是没有那个必要,因为有个简单公式可以直接计算。
公式:总数 = (右值 - 左值 - 1) / 2
例如:
行政总监的子孙部门数=(18-9-1)/2=4董事长的子孙部门数=(20-1-1)/2=9会计的子部门数=(14-13-1)/2=0可以数数看,确实没错哦~
判断是否叶子节点
通过有了上述计算公式算总数的经验后,现在判断是否叶子节点,有的小伙伴已经知道了怎么做,那就是:
右值 - 1 == 左值那他就是叶子节点,或者左值 + 1 == 右值那他就是叶子节点,反之则不是叶子节点。
例如:
设计部,5 - 1 == 4,因此他是叶子节点。
董事长,20 - 1 != 1,因此他不是叶子节点。
至此已经完美的解决了上述需求问题,接下来再尝试一下业务的基本操作。
其他基本操作
新增部门
当新增一个部门时,需要对新增节点位置的后续边缘进行加2操作,因为每一个节点有左右两个数值。这个操作通常需要放到事务中进行处理。例如:在研发部门下添加一个新部门:
对应sql:
SET@lft:=7;/*新部门的左值*/SET@rgt:=8;/*新部门的左值*/SET@level:=5;/*新部门的层级*/begin;/*将插入的后续边缘的节点左右数+2*/UPDATEdepartmentSETlft=lft+2WHERElft>@lft;UPDATEdepartmentSETrgt=rgt+2WHERErgt>=@lft;/*插入数据*/INSERTINTOdepartment(name,lft,rgt,level)VALUES("新部门",@lft,@rgt,level);/*新增影响行数为0时,必须回滚*/commit;/*rollback;*/
删除部门
删除部门与新增部门类似,不同的是需要对删除节点的后续边缘节点减2操作。例如:删除刚刚添加的新部门:
对应sql
SET@lft:=7;/*要删除的节点左值*/SET@rgt:=8;/*要删除的节点右值*/begin;UPDATEdepartmentSETlft=lft-2WHERElft>@lft;UPDATEdepartmentSETrgt=rgt-2WHERErgt>@lft;/*删除节点*/DELETEFROMdepartmentWHERElft=@lftANDrgt=@rgt;/*删除影响行数为0时,必须回滚*/commit;/*rollback*/
查询直接子部门
查询某部门的直接子部门(即不包含孙子部门),例如:查询总经理下的直接子部门。正常需要返回产品部和行政总监
对应的sql
SET@level:=2;/*总经理的level*/SET@lft:=2;/*总经理的左值*/SET@rgt:=19;/*总经理的右值*/SELECT*FROMdepartmentWHERElft>@lftANDrgt<@rgtANDlevel=@level+1;
查询祖链路径
查询某部门的祖链路径。例如:查询产品部的祖链路径,正常需要返回董事长,总经理
SET@lft:=3;/*产品部左值*/SET@rgt:=8;/*产品部右值*/SELECT*FROMdepartmentWHERElft<@lftANDrgt>@rgtORDERBYlftASC;
树形数据展示(JS示例)
letlist=[//模拟sql查出来的列表。{id:1,name:"root",lft:1,rgt:8,level:1},{id:2,name:"child",lft:2,rgt:7,level:2},{id:3,name:"grandson",lft:3,rgt:4,level:3},{id:4,name:"grandson2",lft:5,rgt:6,level:3}];letrights=[]/*类似于一个栈结构(后进先出)*/letmp={}//list.sort((a,b)=> a.lft - b.lft)//如果你在sql中没有进行排序,需要在这里给他排序。list.forEach(item=>{if(rights.length>0){while(rights[rights.length-1]{let_tree=[];_list.forEach(item=>{if(item.parent_id==parent_id){letchilds=recursive(_list,item.id)_tree.push({...item,children:childs.length>0?childs:(item.isLeaf?null:[])})}})return_tree}console.log(recursive(list))
完结
在我目前看来,这个方法的唯一缺点就是,每一次的新增或删除,操作节点的后续边缘走到的节点都要加/减2操作。
关键词:
(责任编辑:黄俊飞)推荐内容
- 一种避免递归查询的树状数据表设计与实现
- *ST中安: 关于召开2023年第一次临时股东
- 亟需是什么意思_亟需的解释 全球新资讯
- 拉伤筋骨怎样好得快_拉伤筋怎么好的快
- 两寸照片的尺寸是多少px_两寸照片的尺寸
- 不会拒绝的女人 亲亲漫画_不会拒绝的女
- 雄鹿将有五名球员缺席对阵步行者的比赛
- 百度英语翻译器带发音_百度英语翻译器-全
- 焦点讯息:埃尼集团计划今年启动新一轮股
- 世界热资讯!《洛克》
- 清明节武汉扫墓专线汇总 全球实时
- 即时:特种设备2021管理制度新规_特种设
- 世界即时:不负农时不负春 燕赵春耕正当时
- 环球聚焦:坐位体前屈测试方法_坐位体前
- 美容健身培训成预付费纠纷多发区
- 2023国考笔试放榜 面试人员名单将统一公
- 世界各国煤炭储量排行出炉,我国总量是美
- 视频中出现了一个假想的三星GalaxyS21智
- 业绩波动大,盈利能力下行,中亿基业三次
- 研报称“非洲猪瘟或卷土重来”,相关上市
- 每体:巴萨打算先租后买18岁巴西前锋罗克
- 一个时代结束!曝中超八冠王+前亚冠霸主
- 少儿重疾险怎么买_少儿重疾险怎么买更合适
- 梦见女朋友出轨特别真实是什么预兆_梦见
- 全球时讯:徽商职业技术学院(徽商职业技
- 环球简讯:电子车牌板块3月14日跌1.03%,
- 全面注册制下首批10家主板企业IPO提交注册
- 马尔卡宁:儿时喜欢科比&韦德&老詹&德克
- 天天快看点丨马和女子交过程图_马交女
- 珍爱生命!13岁女孩因学业压力大欲跳河轻
- 环球滚动:该文档大于2千兆字节怎么设置_
- 天天报道:叮当健康盘中涨超145%,控股股
- 代扣个税会计分录 全球信息
- 傻姑娘
- 焦点热讯:中国人民政治协商会议第十四届
- 浮山:开展职业技能培训 拓宽群众就业渠
- 明日起,有香港旅居史人士从澳门前往内地
- 全球观察:永顺泰(001338)3月13日主力
- 收益法评估企业价值论文_收益法评估企业
- 全球热消息:英媒:中国“5%左右目标”传
- 【速看料】健康元:未来本公司将继续深化
- 杭州锐拓科技有限公司_关于杭州锐拓科技
- 每日热议!like to do sth.和like doi
- 常熟汽饰收到某客户及大众安徽产品项目定
- 2022中国家庭影音设备总结:彩电零售量规
- 属兔女和什么属相相冲相克_属兔女和什么
- 天天精选!编织的近义词_编织的意思
- 电脑怎么安装手写板_手写板安装方法|全球
- 干燕窝能冷冻保存吗 干燕窝可不可以冷冻
- 全球聚焦:夏天手抄报简单又漂亮三年级_
- par是什么_常见的大par是什么意思 世界观点
- 下周事件前瞻:中国前两月经济数据;美CP
- 艾隆科技(688329)3月10日主力资金净卖
- 透明头像怎么弄全透明_透明头像怎么弄_当
- 异丙醇和酒精哪个更安全_异丙醇的作用
- 明加空的字拼音是什么_明加空念什么
- 世界快看:狼爱上羊吉他谱c调_狼爱上羊
- 全球今头条!物流工程专业就业前景如何_
- 臭豆腐的危害和禁忌_臭豆腐的危害
- 2只科创50指数基金获批 跟踪指数产品规
- 每体:巴萨打算先租后买18岁巴西前锋罗克
- 一个时代结束!曝中超八冠王+前亚冠霸主
- 少儿重疾险怎么买_少儿重疾险怎么买更合适
- 梦见女朋友出轨特别真实是什么预兆_梦见
- 全球时讯:徽商职业技术学院(徽商职业技
- 环球简讯:电子车牌板块3月14日跌1.03%,
- 全面注册制下首批10家主板企业IPO提交注册
- 马尔卡宁:儿时喜欢科比&韦德&老詹&德克
- 天天快看点丨马和女子交过程图_马交女
- 珍爱生命!13岁女孩因学业压力大欲跳河轻
- 环球滚动:该文档大于2千兆字节怎么设置_
- 天天报道:叮当健康盘中涨超145%,控股股
- 代扣个税会计分录 全球信息
- 傻姑娘
- 焦点热讯:中国人民政治协商会议第十四届
- 浮山:开展职业技能培训 拓宽群众就业渠
- 明日起,有香港旅居史人士从澳门前往内地
- 全球观察:永顺泰(001338)3月13日主力
- 收益法评估企业价值论文_收益法评估企业
- 全球热消息:英媒:中国“5%左右目标”传
- 【速看料】健康元:未来本公司将继续深化
- 杭州锐拓科技有限公司_关于杭州锐拓科技
- 每日热议!like to do sth.和like doi
- 常熟汽饰收到某客户及大众安徽产品项目定
- 2022中国家庭影音设备总结:彩电零售量规
- 属兔女和什么属相相冲相克_属兔女和什么
- 天天精选!编织的近义词_编织的意思
- 电脑怎么安装手写板_手写板安装方法|全球
- 干燕窝能冷冻保存吗 干燕窝可不可以冷冻
- 全球聚焦:夏天手抄报简单又漂亮三年级_
- par是什么_常见的大par是什么意思 世界观点
- 下周事件前瞻:中国前两月经济数据;美CP
- 艾隆科技(688329)3月10日主力资金净卖
- 透明头像怎么弄全透明_透明头像怎么弄_当
- 异丙醇和酒精哪个更安全_异丙醇的作用
- 明加空的字拼音是什么_明加空念什么
- 世界快看:狼爱上羊吉他谱c调_狼爱上羊
- 全球今头条!物流工程专业就业前景如何_
- 臭豆腐的危害和禁忌_臭豆腐的危害
- 2只科创50指数基金获批 跟踪指数产品规
- 生命之芯阅读短文_生命之芯
- 微动态丨我从基层来丨全国政协委员魏新:
- 双峰县荷叶镇丰石村:幸福路诞生记 世界
- 【天天播资讯】学修车后悔一辈子_学修车
- 【天天报资讯】DRX队员哭成这样,cvmax仍
- 打完破伤风针注意事项和禁忌_打完破伤风
- 荷兰经济部门预测该国2024年将有近百万居
- 【世界独家】梦幻诛仙手游合欢派怎么样_
- 滁州市南谯区老年学校:“老有所学”见行
- 安溪县检察院简易听证促信访人解心结 环
- 宁波中百:子公司拟1.12亿元将哈尔滨部分
- 美的空调eco模式怎么开启_美的空调eco模式
- 港股异动|艾美疫苗大涨逾16%拟发行不超2.
- 当前消息!勇士季后赛岌岌可危,5换1报价
- w10耳机没有声音是怎么回事_w10前置耳机
- 当前快讯:星之谷什么时候出 公测上线时
- stdev是什么函数公式_stdev是什么意思-环
- 云南旅行路线攻略 云南旅游的路线和攻略
- 世界实时:深水规院(301038.SZ):被确
- Mysteel日报:8日国内钢价涨势延续 市场