超赞,老外的一种避免递归查询所有子部门的树数据表设计与实现!
点击上方蓝色字体,选择“设为星标”
文章来源:https://sourl.cn/aCCTwr
问题来了
查出所有子孙部门
查询子孙部门总数
判断是否叶子节点
要不试试这个方法?
查出所有子孙部门
查询子孙部门总数
判断是否叶子节点
其他基本操作
完结
id 部门编号
name 部门名称
level 所在树层级
parent_id 上级部门编号
| 问题来了
例如:PM加了以下需求:
查出指定部门下所有子孙部门 查询子孙部门总数 判断节点是否叶子节点
查出所有子孙部门
查询子孙部门总数
判断是否叶子节点
| 要不试试这个方法?
查出所有子孙部门
SET @lft := 9;
SET @rgt := 18;
SELECT * FROM department WHERE lft BETWEEN @lft AND @rgt ORDER BY lft ASC;
/*例子中用BETWEEN将被查部门本身也查了出来。实际中可以用大于小于*/完美~
查询子孙部门总数
行政总监的子孙部门数 = (18 - 9 - 1) / 2 = 4
董事长的子孙部门数 = (20 - 1 - 1) / 2 = 9
会计的子部门数 = (14 - 13 - 1) / 2 = 0
可以数数看,确实没错哦~
判断是否叶子节点
| 其他基本操作
新增部门
SET @lft := 7;/*新部门的左值*/
SET @rgt := 8;/*新部门的左值*/
SET @level := 5;/*新部门的层级*/
begin;
/*将插入的后续边缘的节点左右数+2*/
UPDATE department SET lft=lft+2 WHERE lft > @lft;
UPDATE department SET rgt=rgt+2 WHERE rgt >= @lft;
/*插入数据*/
INSERT INTO department(name,lft,rgt,level) VALUES('新部门',@lft,@rgt,level);
/*新增影响行数为0时,必须回滚*/
commit;
/*rollback;*/
删除部门
SET @lft := 7;/*要删除的节点左值*/
SET @rgt := 8;/*要删除的节点右值*/
begin;
UPDATE department SET lft=lft-2 WHERE lft > @lft;
UPDATE department SET rgt=rgt-2 WHERE rgt > @lft;
/*删除节点*/
DELETE FROM department WHERE lft=@lft AND rgt=@rgt;
/*删除影响行数为0时,必须回滚*/
commit;
/*rollback*/
查询直接子部门
SET @level := 2;/*总经理的level*/
SET @lft := 2;/*总经理的左值*/
SET @rgt := 19;/*总经理的右值*/
SELECT * FROM department WHERE lft > @lft AND rgt < @rgt AND level = @level+1;
查询祖链路径
SET @lft := 3;/*产品部左值*/
SET @rgt := 8;/*产品部右值*/
SELECT * FROM department WHERE lft < @lft AND rgt > @rgt ORDER BY lft ASC;
树形数据展示(JS示例)
let list = [//模拟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}
];
let rights = [] /*类似于一个栈结构(后进先出)*/
let mp = {}
//list.sort((a,b) => a.lft - b.lft)//如果你在sql中没有进行排序,需要在这里给他排序。
list.forEach(item => {
if(rights.length > 0) {
while(rights[rights.length-1] < item.rgt) {
rights.splice(-1, 1)//从rights末尾去除
}
}
let _level = rights.length;
item._level = _level;
mp[_level] = item.id
item.parent_id = _level - 1 in mp ? mp[_level - 1] : null;//计算出上级部门编号
item.is_leaf = item.lft === item.rgt - 1;//判断是否叶子部门
rights.push(item.rgt)
})
/*上级部门计算出来了,和存parent_id的效果就一样了,后面只需要递归即可*/
/*递归函数 示例*/
let recursive = (_list, parent_id = null) => {
let _tree = [];
_list.forEach(item => {
if(item.parent_id == parent_id) {
let childs = recursive(_list, item.id)
_tree.push({
...item,
children: childs.length > 0 ? childs : (item.isLeaf ? null : [])
})
}
})
return _tree
}
console.log(recursive(list))
| 完结
评论