通常情况下,我们会从外部系统或者其他数据源得到以下树形结构的数据,并需要对其进行处理

其中,需要做的处理包括

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,并递归累计求父科目的金额的更多相关文章

  1. C#递归累计到父行

    搞了半天 写了一个算法,希望能帮到需要的朋友 效果如下 水电费用是由 就是部门水费和电费累加的,而部门水费由科室水费累加起来的 表结构 DataTable dt = new DataTable(); ...

  2. Thinkphp 获取所有子分类或父分类ID

    /** * @Author: HTL * @Email: Huangyuan413026@163.com * @DateTime: 2016-04-22 11:25:02 * @Description ...

  3. JavaScript通过父节点ID递归生成JSON树

    JavaScript通过父节点ID递归生成JSON树: · 实现思路:通过递归实现(第一次递归的时候查询出所有的父节点,然后通过当前父节点id不断地去查询所有子节点,直到递归完毕返回)   · 代码示 ...

  4. 使用Oracle数据库实现树形结构表的子-父级迭代(递归)查询和删除,通过级联菜单简单举例

    前言: 我们在开发中,常常遇到单表的子-父id级联的表结构,在树形的深度不确定的情况下,一次查询出某个树形结构下的所有具有子-父级关系的数据变得十分困难. 这时,我们使用oracle提供的CONNEC ...

  5. sql实现通过父级id查询所有的子集

    通过sql实现传入父级id查询出所有的子集 最近刚好有个业务需要这样实现个功能,就是在点击查询列表详情的时候只会传入父级id,而详情得渲染出所有子集,那么做法有很多,可以直接通过代码递归查询去实现, ...

  6. JavaScript之递归查找所有父节点

    ......data: () => ({ // 数据 dt: [{ id: '1', children: [ { id: '1-1', children: [ { id: '1-1-1', ch ...

  7. With As 获取 id parentId 递归获取所有

    Declare @Id Int  Set @Id = 5;    ---在此修改父节点    With RootNodeCTE(Id,ParentId)  As  (  Select Id,Paren ...

  8. vue_elementUI_ tree树形控件 获取选中的父节点ID

    el-tree 的 this.$refs.tree.getCheckedKeys() 只可以获取选中的id 无法获取选中的父节点ID想要获取选中父节点的id;需要如下操作1. 找到工程下的node_m ...

  9. 获取进程ID,父进程ID,进程完整路径

    准备写一个进程管理的功能模块,今天下午先写了扫描获取本机各个进程路径,获取各个进程映像名称,进程完整路径. 要获取进程信息,第一步想到的就是提权,提权代码用过多次了,今天也小结了一下(http://w ...

随机推荐

  1. 杭电hdu-6168 Numbers

    这一题是考察排序与后续数据处理的题.我是用了map来给“和”做标记,把确定为a数组内数的数两两求和.给这些和标记,这样就可以很好的处理带有重复数的数据了~~ 贴个碼: #include<iost ...

  2. .NET控件集ComponentOne 2018V3发布:新增图表动画及迷你图

    “通过使用 ComponentOne .NET控件产品,实现了兼具 BS 架构灵活性与 CS 架构的客户体验.丰富的控件满足了项目中的各种特殊需求,使得开发的精力可以专注于业务逻辑,为团队节省了时间. ...

  3. Linux 命令之mv

    mv命令也是Linux中很常见命令 作用:可以用来移动文件或者将文件改名 命令格式: mv [选项] 源文件或目录 目标文件或目录 命令参数: -b :若需覆盖文件,则覆盖前先行备份. -f :for ...

  4. 集成算法——Ensemble learning

    目的:让机器学习效果更好,单个不行,群殴啊! Bagging:训练多个分类器取平均 Boosting:从弱学习器开始加强,通过加权来进行训练 (加入一棵树,比原来要强) Stacking:聚合多个分类 ...

  5. 关于JAVA中包装类的是什么类型传递这个问题的笔记

    背景知识: 如果参数类型是原始类型,那么传过来的就是这个参数的一个副本,也就是这个原始参数的值.如果在函数中改变了副本的值不会改变原始的值. 如果参数类型是引用类型,那么传过来的就是这个参数的引用,这 ...

  6. 随机--相关(Fisher_Yates算法)

    Fisher_Yates算法   void ShuffleArray_Fisher_Yates(char* arr, int len) {     int i = len, j;     char t ...

  7. 在nodejs中的集成虹软人脸识别

    ==虹软官网地址==http://www.arcsoft.com.cn 在官网注册账号,并且申请人脸识别激活码, 选择SDK版本和运行系统(windows/linux/android/ios) ,我们 ...

  8. Spark多种运行模式

    1.测试或实验性质的本地运行模式(单机) 该模式被称为Local[N]模式,是用单机的多个线程来模拟Spark分布式计算,通常用来验证开发出来的应用程序逻辑上是否有问题. 其中N代表可以使用N个线程, ...

  9. JavaScript学习第一天(一)

    JavaScript介绍 JavaScript一种直译式脚本语言,是一种动态类型.弱类型.基于原型的语言,内置支持类型.它的解释器被称为JavaScript引擎,为浏览器的一部分,广泛用于客户端的脚本 ...

  10. 雇佣K个工人的最小费用 Minimum Cost to Hire K Workers

    2018-10-06 20:17:30 问题描述: 问题求解: 问题规模是10000,已经基本说明是O(nlogn)复杂度的算法,这个复杂度最常见的就是排序算法了,本题确实是使用排序算法来进行进行求解 ...