根据科目计算父科目ID,并递归累计求父科目的金额
通常情况下,我们会从外部系统或者其他数据源得到以下树形结构的数据,并需要对其进行处理

其中,需要做的处理包括
1.计算每个科目的父科目ID,即PARENT_ID;
2.计算每个科目的ITEM_LEVEL;
3.判断每个节点是否叶子节点;
4.计算父科目的金额。
建表如下
create table CUX.CUX_TEST
(
account_id number,
parent_id number,
account_code varchar2(30),
item_level number,
leaf_flag varchar2(1),
amount number
);
导入数据
insert into cux.cux_test (ACCOUNT_ID, PARENT_ID, ACCOUNT_CODE, ITEM_LEVEL, LEAF_FLAG, AMOUNT)
values (1, null, '', null, null, 0);
insert into cux.cux_test (ACCOUNT_ID, PARENT_ID, ACCOUNT_CODE, ITEM_LEVEL, LEAF_FLAG, AMOUNT)
values (2, null, '1.1', null, null, 0);
insert into cux.cux_test (ACCOUNT_ID, PARENT_ID, ACCOUNT_CODE, ITEM_LEVEL, LEAF_FLAG, AMOUNT)
values (3, null, '1.1.1', null, null, 0);
insert into cux.cux_test (ACCOUNT_ID, PARENT_ID, ACCOUNT_CODE, ITEM_LEVEL, LEAF_FLAG, AMOUNT)
values (4, null, '1.1.1.1', null, null, 200);
insert into cux.cux_test (ACCOUNT_ID, PARENT_ID, ACCOUNT_CODE, ITEM_LEVEL, LEAF_FLAG, AMOUNT)
values (5, null, '1.1.1.2', null, null, 100);
insert into cux.cux_test (ACCOUNT_ID, PARENT_ID, ACCOUNT_CODE, ITEM_LEVEL, LEAF_FLAG, AMOUNT)
values (6, null, '1.1.2', null, null, 0);
insert into cux.cux_test (ACCOUNT_ID, PARENT_ID, ACCOUNT_CODE, ITEM_LEVEL, LEAF_FLAG, AMOUNT)
values (7, null, '1.1.2.1', null, null, 80);
insert into cux.cux_test (ACCOUNT_ID, PARENT_ID, ACCOUNT_CODE, ITEM_LEVEL, LEAF_FLAG, AMOUNT)
values (8, null, '1.1.3', null, null, 50);
insert into cux.cux_test (ACCOUNT_ID, PARENT_ID, ACCOUNT_CODE, ITEM_LEVEL, LEAF_FLAG, AMOUNT)
values (9, null, '', null, null, 0);
insert into cux.cux_test (ACCOUNT_ID, PARENT_ID, ACCOUNT_CODE, ITEM_LEVEL, LEAF_FLAG, AMOUNT)
values (10, null, '2.1', null, null, 0);
insert into cux.cux_test (ACCOUNT_ID, PARENT_ID, ACCOUNT_CODE, ITEM_LEVEL, LEAF_FLAG, AMOUNT)
values (11, null, '2.1.1', null, null, 40);
insert into cux.cux_test (ACCOUNT_ID, PARENT_ID, ACCOUNT_CODE, ITEM_LEVEL, LEAF_FLAG, AMOUNT)
values (12, null, '2.1.2', null, null, null);
insert into cux.cux_test (ACCOUNT_ID, PARENT_ID, ACCOUNT_CODE, ITEM_LEVEL, LEAF_FLAG, AMOUNT)
values (13, null, '2.1.2.1', null, null, null);
处理数据:
先处理前三步,即:
1.计算每个科目的父科目ID,即PARENT_ID;
2.计算每个科目的ITEM_LEVEL;
3.判断每个节点是否叶子节点;
-- Created on 2018/1/27 by ADMINISTRATOR
DECLARE CURSOR cur_data IS
SELECT * FROM cux.cux_test ct ORDER BY ct.account_id; l_item_level NUMBER;
l_parent_id NUMBER;
l_delimiter_count NUMBER;
l_parent_code cux.cux_test.account_code%TYPE; BEGIN --更新科目层级关系
--更新 cux_test 中的parent_id,item_level,leaf_flag
FOR cc IN cur_data LOOP
--先将所有科目都默认为叶子节点,之后再更新为非叶子节点
UPDATE cux.cux_test ct
SET ct.leaf_flag = 'Y'
WHERE ct.account_id = cc.account_id; /*l_delimiter_count := nvl(length(regexp_replace(cc.account_code,
'[0-9]')),
0);*/
--利用分隔符数量+1
l_item_level := nvl(length(regexp_replace(cc.account_code, '[0-9]')), 0) + 1; IF l_item_level = 1 THEN
l_parent_id := 0;
UPDATE cux.cux_test ct
SET ct.leaf_flag = 'N'
WHERE ct.account_id = cc.account_id;
ELSE
--非一级科目寻找父级科目CODE
l_parent_code := substr(cc.account_code,
1,
instr(cc.account_code, '.', -1) - 1);
--父科目ID
SELECT ct.account_id
INTO l_parent_id
FROM cux.cux_test ct
WHERE ct.account_code = l_parent_code; --更新父科目对应的叶子标记
UPDATE cux.cux_test ct
SET ct.leaf_flag = 'N'
WHERE ct.account_id = l_parent_id; END IF; UPDATE cux.cux_test cct
SET cct.item_level = l_item_level, cct.parent_id = l_parent_id
WHERE cct.account_id = cc.account_id; END LOOP;
COMMIT; END;
最后处理
4.计算父科目的金额。
贡献一个比较有效的针对这种父子结构的求和累计函数
该计算方式是会把叶子节点和父节点本身的值都计算进去
CREATE OR REPLACE FUNCTION recursive_amount_add(root_id IN NUMBER)
RETURN NUMBER IS
/*根据实际情况,判断是否需要做nvl(amount,0)处理,此函数对于子节点为空的仍能优雅的处理*/
/*此处需要使用UNION ALL,否则当叶子节点金额相等时就不会重复计算了*/
total NUMBER;
BEGIN
SELECT SUM(amount)
INTO total
FROM ((SELECT ct.amount
FROM cux.cux_test ct
WHERE ct.account_id = root_id) UNION ALL
(SELECT recursive_amount_add(ct.account_id) amount
FROM cux.cux_test ct
WHERE ct.parent_id = root_id));
RETURN total;
END;
参考:Recursive sum of values in an hierarchical table in Oracle 10g
如果仅仅需要计算叶子节点之和
CREATE OR REPLACE FUNCTION recursive_amount_add(root_id IN NUMBER)
RETURN NUMBER IS
/*根据实际情况,判断是否需要做nvl(amount,0)处理,此函数对于子节点为空的仍能优雅的处理*/
total NUMBER;
BEGIN
SELECT SUM(amount)
INTO total
FROM ((SELECT ct.amount
FROM cux.cux_test ct
WHERE ct.account_id = root_id
AND ct.leaf_flag = 'Y') UNION ALL
(SELECT recursive_amount_add(ct.account_id) amount
FROM cux.cux_test ct
WHERE ct.parent_id = root_id));
RETURN total;
END;
查询结果如下:
SELECT ct.*, recursive_amount_add(ct.account_id) recursive_sum FROM cux.cux_test ct

注意:如果需要直接更新amount的值,不能直接使用该函数进行update(在递归过程中amount的值已经改变,函数失效),需将其作为查询结果集再进行更新。
方式1:直接使用Loop循环:
BEGIN
FOR cc IN (SELECT ct.account_id,
recursive_amount_add(ct.account_id) recursive_sum
FROM cux.cux_test ct) LOOP
UPDATE cux.cux_test ct
SET ct.amount = cc.recursive_sum
WHERE ct.account_id = cc.account_id;
END LOOP;
COMMIT;
END;
方式2:使用数组bulk collect,(多一种方法,可能运用到其他场景)
bulk collect的更多用法,在我的另一篇文章略有简述:使用Bulk Binding批量绑定的模式高效处理ORACLE大量数据
DECLARE
TYPE recursive_record IS RECORD(
account_id NUMBER,
recursive_sum NUMBER);
TYPE recursive_type IS TABLE OF recursive_record;
recur_tab recursive_type; BEGIN SELECT ct.account_id, recursive_amount_add(ct.account_id) recursive_sum
BULK COLLECT
INTO recur_tab
FROM cux.cux_test ct; FOR i IN recur_tab.first .. recur_tab.last LOOP
UPDATE cux.cux_test ct
SET ct.amount = recur_tab(i).recursive_sum
WHERE ct.account_id = recur_tab(i).account_id; END LOOP;
recur_tab.delete; COMMIT; END;
根据科目计算父科目ID,并递归累计求父科目的金额的更多相关文章
- C#递归累计到父行
搞了半天 写了一个算法,希望能帮到需要的朋友 效果如下 水电费用是由 就是部门水费和电费累加的,而部门水费由科室水费累加起来的 表结构 DataTable dt = new DataTable(); ...
- Thinkphp 获取所有子分类或父分类ID
/** * @Author: HTL * @Email: Huangyuan413026@163.com * @DateTime: 2016-04-22 11:25:02 * @Description ...
- JavaScript通过父节点ID递归生成JSON树
JavaScript通过父节点ID递归生成JSON树: · 实现思路:通过递归实现(第一次递归的时候查询出所有的父节点,然后通过当前父节点id不断地去查询所有子节点,直到递归完毕返回) · 代码示 ...
- 使用Oracle数据库实现树形结构表的子-父级迭代(递归)查询和删除,通过级联菜单简单举例
前言: 我们在开发中,常常遇到单表的子-父id级联的表结构,在树形的深度不确定的情况下,一次查询出某个树形结构下的所有具有子-父级关系的数据变得十分困难. 这时,我们使用oracle提供的CONNEC ...
- sql实现通过父级id查询所有的子集
通过sql实现传入父级id查询出所有的子集 最近刚好有个业务需要这样实现个功能,就是在点击查询列表详情的时候只会传入父级id,而详情得渲染出所有子集,那么做法有很多,可以直接通过代码递归查询去实现, ...
- JavaScript之递归查找所有父节点
......data: () => ({ // 数据 dt: [{ id: '1', children: [ { id: '1-1', children: [ { id: '1-1-1', ch ...
- With As 获取 id parentId 递归获取所有
Declare @Id Int Set @Id = 5; ---在此修改父节点 With RootNodeCTE(Id,ParentId) As ( Select Id,Paren ...
- vue_elementUI_ tree树形控件 获取选中的父节点ID
el-tree 的 this.$refs.tree.getCheckedKeys() 只可以获取选中的id 无法获取选中的父节点ID想要获取选中父节点的id;需要如下操作1. 找到工程下的node_m ...
- 获取进程ID,父进程ID,进程完整路径
准备写一个进程管理的功能模块,今天下午先写了扫描获取本机各个进程路径,获取各个进程映像名称,进程完整路径. 要获取进程信息,第一步想到的就是提权,提权代码用过多次了,今天也小结了一下(http://w ...
随机推荐
- Vue学习二:v-model指令使用方法
本文为博主原创,未经允许不得转载: <!DOCTYPE html> <html lang="zh"> <head> <script src ...
- window下的Django入门
一.window下新建安装(参考书籍:<python编程:从入门到实践>) 新建一个文件夹 learning_log ,在终端中切换到该目录下,并创建一个虚拟工作环境,运行模块 venv ...
- Ural 1297 Palindrome(后缀数组+最长回文子串)
https://vjudge.net/problem/URAL-1297 题意: 求最长回文子串. 思路: 先将整个字符串反过来写在原字符串后面,中间需要用特殊字符隔开,那么只需要某两个后缀的最长公共 ...
- Easy Graphics Engine vs2015使用
vs2017: 下载 https://pan.baidu.com/s/1qWxAgeK 里面的 “ege19.01_vs2017 (推荐, 修正win10 1809 上无法正常运行的问题).zip” ...
- Django模板操作
进行加减运算 def index(request): a = request.GET['a'] b = request.GET['b'] c = int(a) + int(b) return Http ...
- Java String删除字符串中间的某部分
当你想删除字符串中的某部分时,java中并没有直接提供相关的方法,比如想删除 "cout<<\"Hello world\"<<endl" ...
- IPC 之 ContentProvider 的使用
一.概述 ContentProvider 是 Android 中提供的专门用于不同应用间进行数据共享的方式.和 Messenger 一样,ContentProvider 的底层实现同样也是 Binde ...
- nodejs + ts 配置
参考:https://github.com/nestjs/typescript-starter 和 How to get auto restart and breakpoint support wit ...
- QT新建工程编译出现"Moc'ing xxx.h..." 找不到文件或者error MSB6006: "cmd.exe" exited with code 3.
产生这个问题的原因是QT的头文件路径包含出错: (1)如果没有添加如图所示的路径,则添加后重新编译: (2)如果已经添加了,则删除后,重新添加,在重新编译: (3)或者在QT Project Sett ...
- Linux中计划任务执行脚本crontab-简洁版
我使用的是ubuntu16,所以在ubuntu中一切正常,在其他linux系统中应该都差不多. 1 计划任务,crontab命令选项: -u指定一个用户, -l列出某个用户的任务计划, ...