当前位置: 首页 > news >正文

Oracle 递归 + Decode + 分组函数实现复杂树形统计进阶(第二课)

在上篇文章基础上,我们进一步解决层级数据递归汇总问题 —— 让上级部门的统计结果自动包含所有下级部门数据(含多级子部门),并新增请假天数大于 3 天的统计维度。通过递归 CTE、DECODE函数与分组函数的深度结合,实现真正意义上的树形结构数据聚合。

一、业务需求升级:层级汇总与新增统计维度

核心目标

  1. 递归汇总:上级部门数据包含所有直属 / 非直属下级部门数据(如集团总部需汇总技术研发部、产品运营部及其子部门数据)
  2. 新增统计项:统计每个部门(含各级上级)的 "请假天数 > 3 天" 的记录数
  3. 兼容原有指标:保留请假类型天数统计、状态分类统计

数据模型扩展(无需修改表结构,新增计算逻辑)

-- 新增判断逻辑:请假天数>3天标记DECODE(SIGN(leave_days - 3), 1, 1, 0) AS over_3_days_flag-- SIGN函数说明:返回1(正数)、0(零)、-1(负数),简化条件判断

二、关键技术升级:双向递归 CTE 构建层级关系

1. 递归 CTE 重构:获取每个部门的所有后代部门

WITH dept_ancestor AS (-- 初始层:每个部门自身作为祖先SELECTdept_id,dept_name,parent_dept_id,dept_id AS ancestor_id -- 核心字段:标记当前部门的顶层祖先FROM t_deptUNION ALL-- 递归层:向下遍历子部门,继承祖先IDSELECTd.dept_id,d.dept_name,d.parent_dept_id,da.ancestor_id -- 子部门继承父部门的祖先IDFROM t_dept dJOIN dept_ancestor da ON d.parent_dept_id = da.dept_id)
  • 核心逻辑:为每个部门生成从自身到所有后代的层级路径,ancestor_id表示当前统计的顶层部门(如子部门 4 的 ancestor_id 可为自身 4、父部门 2、根部门 1)
  • 递归方向:从父部门到子部门的向下递归,确保每个子部门关联到所有上级祖先

2. 关联请假表与递归 CTE

SELECTda.ancestor_id, -- 统计的目标部门(上级部门)da_dept.dept_name, -- 目标部门名称tl.dept_id AS child_dept_id -- 实际产生数据的子部门ID(用于验证层级)FROM dept_ancestor daLEFT JOIN t_dept da_dept ON da.ancestor_id = da_dept.dept_id -- 关联祖先部门信息LEFT JOIN t_leave tl ON da.dept_id = tl.dept_id -- 关联子部门请假数据-- 示例输出:祖先部门1(集团总部)会关联到子部门2、3、4、5的所有请假记录

三、DECODE 函数进阶:多维度条件聚合

1. 新增 "请假 > 3 天" 统计

SUM(DECODE(SIGN(tl.leave_days - 3), 1, 1, 0)) AS over_3_days_count-- 等价于:SUM(CASE WHEN tl.leave_days > 3 THEN 1 ELSE 0 END)-- DECODE优势:通过数值比较简化条件表达式,执行效率更高

2. 全维度统计表达式(整合新旧需求)

SELECTda_dept.dept_name AS 部门名称,-- 请假类型统计(含下级部门)SUM(DECODE(tl.leave_type, '年假', tl.leave_days, 0)) AS 年假总天数,SUM(DECODE(tl.leave_type, '事假', tl.leave_days, 0)) AS 事假总天数,SUM(DECODE(tl.leave_type, '病假', tl.leave_days, 0)) AS 病假总天数,-- 状态统计SUM(DECODE(tl.leave_status, '完成', 1, 0)) AS 完成请假数,SUM(DECODE(tl.leave_status, '进行中', 1, 0)) AS 进行中请假数,-- 新增统计:请假>3天SUM(DECODE(SIGN(tl.leave_days - 3), 1, 1, 0)) AS 超3天请假数

四、终极 SQL:递归汇总全层级数据

完整实现代码如下:

相信我,如果你能学会下面这个SQL的写法或者能看懂,那么你对ORACLE类似逻辑的处理已经达到极高的水平,这个SQL我认为有95%的人会看不懂。如果你们都会了欢迎留言打脸。因为这个SQL的实现我在7年前专门拿出来给全公司技术人员进行过培训,留了一个类似的作业,结果1个完成的都没有。你也可以考虑下,如果不用SQL来实现,而是让你去通过代码去实现这个需求的统计,你需要写多少代码来实现,需要多少时间?


WITH dept_ancestor AS (-- 构建部门层级关系,获取每个部门的所有祖先路径SELECTdept_id,dept_name,parent_dept_id,dept_id AS ancestor_id -- 初始祖先为自身FROM t_deptUNION ALL-- 递归向下遍历子部门,继承祖先IDSELECTd.dept_id,d.dept_name,d.parent_dept_id,da.ancestor_id -- 子部门的祖先与父部门一致FROM t_dept dJOIN dept_ancestor da ON d.parent_dept_id = da.dept_id),-- 提取祖先部门的基础信息(避免重复计算)ancestor_info AS (SELECT DISTINCT ancestor_id, dept_nameFROM dept_ancestor)SELECTai.dept_name AS 部门名称,-- 请假类型汇总(含所有子部门)SUM(DECODE(tl.leave_type, '年假', tl.leave_days, 0)) AS 年假总天数,SUM(DECODE(tl.leave_type, '事假', tl.leave_days, 0)) AS 事假总天数,SUM(DECODE(tl.leave_type, '病假', tl.leave_days, 0)) AS 病假总天数,-- 状态汇总SUM(DECODE(tl.leave_status, '完成', 1, 0)) AS 完成请假数,SUM(DECODE(tl.leave_status, '进行中', 1, 0)) AS 进行中请假数,-- 新增统计项SUM(DECODE(SIGN(tl.leave_days - 3), 1, 1, 0)) AS 超3天请假数FROM ancestor_info aiLEFT JOIN dept_ancestor da ON ai.ancestor_id = da.ancestor_idLEFT JOIN t_leave tl ON da.dept_id = tl.dept_id -- 关联子部门请假数据GROUP BY ai.ancestor_id, ai.dept_nameORDER BY ai.ancestor_id;

执行结果解析(新增示例数据后)

部门名称

年假总天数

事假总天数

病假总天数

完成请假数

进行中请假数

超 3 天请假数

集团总部

8.5

2.0

3.0

2

3

2

技术研发部

4.5

2.0

0.0

1

2

2

产品运营部

1.5

0.0

3.0

1

1

0

后端开发组

3.5

0.0

0.0

1

1

1

前端开发组

0.0

2.0

0.0

0

1

1

核心逻辑拆解

1、递归 CTE 双向关联

        向上:每个部门作为祖先,向下遍历所有子部门(ancestor_id固定为顶层部门)

        向下:通过da.dept_id = tl.dept_id关联子部门的实际数据,确保上级部门能获取所有下级数据

2、DECODE 的多维应用

        类型统计:按leave_type分类累加天数

        状态统计:按leave_status分类计数

        数值判断:通过SIGN函数简化 "大于 3 天" 的条件转换

3、分组策略

        按ancestor_id分组,确保每个上级部门汇总其所有后代(包括多级子部门)的数据

        LEFT JOIN确保无数据部门(如根部门若自身无数据)仍能显示统计结果

五、与上篇文章的核心区别

特性

上篇文章(单部门统计)

本文(递归层级统计)

统计范围

仅当前部门或指定子部门

包含所有下级部门(多级递归)

递归方向

单向向下(固定根部门)

双向关联(每个部门可作为祖先)

核心字段

dept_id直接分组

ancestor_id递归分组

新增功能

请假天数 > 3 天统计、层级汇总

六、性能优化与注意事项

1. 索引优化建议

-- 为部门层级关系创建索引CREATE INDEX idx_dept_parent ON t_dept(parent_dept_id);-- 为请假表关联字段创建索引CREATE INDEX idx_leave_dept ON t_leave(dept_id);

2. 大数据量处理

  • 若部门层级超过 1000 层,需调整 Oracle 递归限制:
ALTER SESSION SET MAX_RECURSION_DEPTH = 2000; -- 默认1000层

3. DECODE vs CASE WHEN 扩展

  • 复杂范围判断(如BETWEEN)建议用CASE WHEN,等值判断优先用DECODE
  • 多层嵌套时注意DECODE的参数顺序(严格按匹配顺序执行)

七、总结:树形数据统计的终极解决方案

通过递归 CTE 构建层级关系+DECODE 实现条件聚合+分组函数完成数据汇总,我们实现了:

        1、真正的层级递归统计:上级部门自动包含所有下级数据,支持任意深度的组织架构

        2、多维度复杂计算:在单个 SQL 中完成类型统计、状态分类、数值判断等多重逻辑

        3、代码极简主义:相比传统 Java 递归 + 多层循环,SQL 代码量减少 90% 以上,且执行效率更高

        这种方案特别适合组织架构复杂、层级统计频繁的企业级应用(如人力资源管理、财务成本分摊等场景)。掌握递归与DECODE的组合使用,能让你在处理树形数据时如虎添翼,真正发挥 Oracle 数据库的原生优势。如果你能学会这种SQL逻辑,相信我,肯定会对你在实际工作中有巨大帮助。欢迎关注留言,期待与您一起进步。

相关文章:

  • 基于Pandas和FineBI的昆明职位数据分析与可视化实现(三)- 职位数据统计分析
  • TCP/IP模型、OSI模型与C# Socket编程详解
  • (LeetCode 每日一题) 1498. 满足条件的子序列数目 (双指针)
  • Leetcode 3600. Maximize Spanning Tree Stability with Upgrades
  • ROS的可视化工具rviz介绍
  • 第十一节:Vben Admin 最新 v5.0 (vben5) 快速入门 - 角色菜单授权
  • 智能座舱HMI语音自动化测试技术和实现
  • Linux下的调试器-gdb(16)
  • 【Canvas与标志】圆规脚足球俱乐部标志
  • 机器学习在智能供应链中的应用:需求预测与库存优化
  • RAG的“排毒”指南:告别非知识内容的干扰,实现精准问答
  • 对象的finalization机制Test
  • 数据库 DML 语句详解:语法与注意事项
  • github上传代码步骤(http)
  • 【Docker基础】Docker容器管理:docker inspect及其参数详解
  • 【学习】《算法图解》第八章学习笔记:平衡树
  • spring:BeanPostProcessor后置处理器介绍
  • AI大模型调用方法
  • FastGPT私有化部署完整指南
  • C++ 第四阶段 STL 容器 - 第一讲:详解 std::vector