题目描述:3482. 分析组织层级 - 力扣(LeetCode)
表:
Employees
+----------------+---------+ | Column Name | Type | +----------------+---------+ | employee_id | int | | employee_name | varchar | | manager_id | int | | salary | int | | department | varchar | +----------------+----------+ employee_id 是这张表的唯一主键。 每一行包含关于一名员工的信息,包括他们的 ID,姓名,他们经理的 ID,薪水和部门。 顶级经理(CEO)的 manager_id 是空的。编写一个解决方案来分析组织层级并回答下列问题:
- 层级:对于每名员工,确定他们在组织中的层级(CEO 层级为
1
,CEO 的直接下属员工层级为2
,以此类推)。- 团队大小:对于每个是经理的员工,计算他们手下的(直接或间接下属)总员工数。
- 薪资预算:对于每个经理,计算他们控制的总薪资预算(所有手下员工的工资总和,包括间接下属,加上自己的工资)。
返回结果表以 层级 升序 排序,然后以预算 降序 排序,最后以 employee_name 升序 排序。
结果格式如下所示。
示例:
输入:
Employees 表:
+-------------+---------------+------------+--------+-------------+ | employee_id | employee_name | manager_id | salary | department | +-------------+---------------+------------+--------+-------------+ | 1 | Alice | null | 12000 | Executive | | 2 | Bob | 1 | 10000 | Sales | | 3 | Charlie | 1 | 10000 | Engineering | | 4 | David | 2 | 7500 | Sales | | 5 | Eva | 2 | 7500 | Sales | | 6 | Frank | 3 | 9000 | Engineering | | 7 | Grace | 3 | 8500 | Engineering | | 8 | Hank | 4 | 6000 | Sales | | 9 | Ivy | 6 | 7000 | Engineering | | 10 | Judy | 6 | 7000 | Engineering | +-------------+---------------+------------+--------+-------------+输出:
+-------------+---------------+-------+-----------+--------+ | employee_id | employee_name | level | team_size | budget | +-------------+---------------+-------+-----------+--------+ | 1 | Alice | 1 | 9 | 84500 | | 3 | Charlie | 2 | 4 | 41500 | | 2 | Bob | 2 | 3 | 31000 | | 6 | Frank | 3 | 2 | 23000 | | 4 | David | 3 | 1 | 13500 | | 7 | Grace | 3 | 0 | 8500 | | 5 | Eva | 3 | 0 | 7500 | | 9 | Ivy | 4 | 0 | 7000 | | 10 | Judy | 4 | 0 | 7000 | | 8 | Hank | 4 | 0 | 6000 | +-------------+---------------+-------+-----------+--------+解释:
- 组织结构:
- Alice(ID:1)是 CEO(层级 1)没有经理。
- Bob(ID:2)和 Charlie(ID:3)是 Alice 的直接下属(层级 2)
- David(ID:4),Eva(ID:5)从属于 Bob,而 Frank(ID:6)和 Grace(ID:7)从属于 Charlie(层级 3)
- Hank(ID:8)从属于 David,而 Ivy(ID:9)和 Judy(ID:10)从属于 Frank(层级 4)
- 层级计算:
- CEO(Alice)层级为 1
- 每个后续的管理层级都会使层级数加 1
- 团队大小计算:
- Alice 手下有 9 个员工(除她以外的整个公司)
- Bob 手下有 3 个员工(David,Eva 和 Hank)
- Charlie 手下有 4 个员工(Frank,Grace,Ivy 和 Judy)
- David 手下有 1 个员工(Hank)
- Frank 手下有 2 个员工(Ivy 和 Judy)
- Eva,Grace,Hank,Ivy 和 Judy 没有直接下属(team_size = 0)
- 预算计算:
- Alice 的预算:她的工资(12000)+ 所有员工的工资(72500)= 84500
- Charlie 的预算:他的工资(10000)+ Frank 的预算(23000)+ Grace 的工资(8500)= 41500
- Bob 的预算:他的工资 (10000) + David 的预算(13500)+ Eva 的工资(7500)= 31000
- Frank 的预算:他的工资 (9000) + Ivy 的工资(7000)+ Judy 的工资(7000)= 23000
- David 的预算:他的工资 (7500) + Hank 的工资(6000)= 13500
- 没有直接下属的员工的预算等于他们自己的工资。
注意:
- 结果先以层级升序排序
- 在同一层级内,员工按预算降序排序,然后按姓名升序排序
数据准备
CREATE TABLE if not exists Employees ( employee_id INT, employee_name VARCHAR(100), manager_id INT, salary INT, department VARCHAR(50) ) Truncate table Employees insert into Employees (employee_id, employee_name, manager_id, salary, department) values ('1', 'Alice', NULL, '12000', 'Executive') insert into Employees (employee_id, employee_name, manager_id, salary, department) values ('2', 'Bob', '1', '10000', 'Sales') insert into Employees (employee_id, employee_name, manager_id, salary, department) values ('3', 'Charlie', '1', '10000', 'Engineering') insert into Employees (employee_id, employee_name, manager_id, salary, department) values ('4', 'David', '2', '7500', 'Sales') insert into Employees (employee_id, employee_name, manager_id, salary, department) values ('5', 'Eva', '2', '7500', 'Sales') insert into Employees (employee_id, employee_name, manager_id, salary, department) values ('6', 'Frank', '3', '9000', 'Engineering') insert into Employees (employee_id, employee_name, manager_id, salary, department) values ('7', 'Grace', '3', '8500', 'Engineering') insert into Employees (employee_id, employee_name, manager_id, salary, department) values ('8', 'Hank', '4', '6000', 'Sales') insert into Employees (employee_id, employee_name, manager_id, salary, department) values ('9', 'Ivy', '6', '7000', 'Engineering') insert into Employees (employee_id, employee_name, manager_id, salary, department) values ('10', 'Judy', '6', '7000', 'Engineering')
分析
①使用recursive进行递归查询 通过引用 CTE 自身不断生成新的结果集,直到满足终止条件主表 employee_id 和 子表manager_id 连接条件 直到employee_id遍历完 通过子表employee_id 记录路径,level记录层级
with recursive t1 as (select employee_id, employee_name, 1 as level, CAST(employee_id AS CHAR) as path, salary, department, manager_id from Employees where manager_id is null union select t2.employee_id, t2.employee_name, 1 + t1.level, concat(t1.path, ',', t2.employee_id) as path, t2.salary, t2.department, t2.manager_id from Employees t2 join t1 on t2.manager_id = t1.employee_id) select * from t1
②根据路径 展开主表和子表 将主表employee_id下的所有子员工都展开
select e.employee_id as m_id, e2.employee_id, e2.salary from t1 e join t1 e2 on e2.path like concat(e.path, '%')
③根据主employee_id 计算team_size团队人数和budget预算
select m_id, (count(m_id) - 1) as team_size, sum(salary) as budget from t3 group by m_id
④将结果进行连接 排序
代码
with recursive t1 as (select employee_id,
employee_name,
1 as level,
CAST(employee_id AS CHAR) as path,
salary,
department,
manager_id
from Employees
where manager_id is null
union
select t2.employee_id,
t2.employee_name,
1 + t1.level,
concat(t1.path, ',', t2.employee_id) as path,
t2.salary,
t2.department,
t2.manager_id
from Employees t2
join t1
on t2.manager_id = t1.employee_id)
# select * from t1
, t3 as (select e.employee_id as m_id, e2.employee_id, e2.salary
from t1 e
join t1 e2 on e2.path like concat(e.path, '%'))
, t4 as (select m_id, (count(m_id) - 1) as team_size, sum(salary) as budget
from t3
group by m_id)
select employee_id, employee_name, level, team_size, budget
from t1
left join t4 on employee_id = m_id
order by level, budget desc, employee_name;
-- 合并
with recursive employee as
(select employee_id, employee_name, manager_id, salary, 1 as level, cast(employee_id as char) as path
from Employees
where manager_id is null
union
select e2.employee_id,
e2.employee_name,
e2.manager_id,
e2.salary,
1 + employee.level,
concat(employee.path, ',', e2.employee_id)
from Employees e2
join employee on
e2.manager_id = employee.employee_id)
# select * from
, t3 as(
select e1.employee_id ,e2.employee_id as emp,e1.employee_name,e1.level,e2.salary from employee e1 left join employee e2 on e2.path like concat(e1.path,'%')
)
select employee_id,employee_name,level,(count(employee_id)-1) as team_size,sum(salary)'budget' from t3
group by employee_id, employee_name,level
order by level,budget desc,employee_name
;
总结
①递归查询基本结构
WITH RECURSIVE cte_name (column_list) AS ( -- 锚成员(初始查询) SELECT ... UNION [ALL] -- 递归成员(递归查询) SELECT ... ) SELECT * FROM cte_name;
- 锚成员:返回初始结果集。
- 递归成员:通过引用 CTE 自身不断生成新的结果集,直到满足终止条件。
②执行顺序分析
递归查询的执行遵循 迭代模型,步骤如下:
步骤 1:执行锚成员
- 锚查询首先执行,生成递归的基础结果集(R0)。
步骤 2:执行递归成员
- 将递归成员应用于上一次迭代的结果集(初始为 R0),生成新的结果集(R1)。
- 去重处理:如果使用
UNION
(而非UNION ALL
),会自动删除重复行。步骤 3:检查终止条件
- 如果新生成的结果集(R1)为空,递归终止。
- 否则,将 R1 作为下一次迭代的输入,重复步骤 2。
步骤 4:合并所有结果
- 递归终止后,将所有迭代的结果集合并(隐式使用
UNION ALL
),返回最终结果。③ 强转为字符串才可以进行拼接
cast(employee_id as char)