根据科目计算父科目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 ...
随机推荐
- C#判断数据类型的简单示例代码
; Console.WriteLine( "i is an int? {0}",i.GetType()==typeof(int)); Console.WriteLine( &quo ...
- 恢复Intellij idea删除的文件
恢复Intellij idea的删除文件方法: 右键单机项目名称---->Local History---->Show History 可以看到历史操作记录,右键单机想要恢复的文件---- ...
- SpringBoot的文件上传
先在src/main/resources下新建一个static目录用以存放html页面,简单的html页面如下 <!DOCTYPE html> <html> <head& ...
- Anroid 搭建Maven私服(Android Studio)
一.场景 ① 公司中有多个项目,多人开发,开发中的某一模块需要被其他几个项目的模块所依赖,最简单粗暴的方式就是打包成aar或者jar文件逐个拷贝到libs下进行依赖,可是这种方式太过于麻烦,而且需要每 ...
- git批量删除文件和批量提交
1. 单个删除文件: ① 通常直接在文件管理器中把没用的文件删了,或者用rm命令删了:(可选操作,可直接执行②删除) $ rm test.txt ② 确实要从版本库中删除该文件,那就用命令git rm ...
- git报错fatal: loose object ....(stored in .git/objects/....) is emtpy
主要是非正常关机.把.git给破坏了 参考https://stackoverflow.com/questions/12571557/fixing-a-corrupt-loose-object-as-a ...
- Altium Designer PCB的时候 高亮显示引脚连线
按住Ctrl ,左击连线,就可以高亮显示两个连接的引脚.
- datagrid数据表格当数据为0的时候页面不显示数据
如下图: datagrid数据表格当数据为0的时候页面不显示数据,为空的表格数据全是0,但是页面无法显示 传递的json数据也是没问题的: 所以实在想不通,为什么easyUI datagrid 不显示 ...
- Hive json字符串解析
在做数据测试时,会遇到数据库表里字段类型为json 的情况,而我们可能只关注该字段中的各别内容的信息,如下 content {"测试内容1":,"测试内容2": ...
- 日常英语---十、MapleStory/Monsters/Level 191-201(Sad Erda:向你发射炮弹进行攻击)
日常英语---十.MapleStory/Monsters/Level 191-201(Sad Erda:向你发射炮弹进行攻击) 一.总结 一句话总结:一种悲伤的精神,形成于奥术之河,一个黑法师能够融合 ...