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

其中,需要做的处理包括

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. Vue学习二:v-model指令使用方法

    本文为博主原创,未经允许不得转载: <!DOCTYPE html> <html lang="zh"> <head> <script src ...

  2. 教你用ActiveReports分析京东双十一数据的价值

    随着双十一购物盛会落下帷幕,各大电商平台纷纷公布出自己今年的成绩.与其它同行不同的是,京东除了公布1598亿的线上下单金额,还公布了线上线下融合的战果. 面对京东线上.线下海量数据源,我们该如何进行整 ...

  3. SAP成本核算说明

    SAP成本核算说明 <SAP财务管控——财务总监背后的管理大师>京东有售. > SAP成本核算说明 说明: 1.    原材料采用移动平均价核算:产成品采用计划(标准)成本核算: 2 ...

  4. 2019年前端面试题 | CSS篇 (更新于4月15日)

    虽说刷面试题有走捷径之嫌,但我发现,对于我这样没有工作经历的人来说,其实是拓展自己实战技能和加深知识理解的一个好机会. 分享出来,也希望大家不要背完了事,正经的去细细琢磨各种原由. 本篇是一个题目合集 ...

  5. Git入门看这一篇就够了! (转)

    Git 的三种状态 Git 有三种状态,你的文件可能处于其中之一: 已提交(committed):数据已经安全的保存在本地数据库中. 已修改(modified):已修改表示修改了文件,但还没保存到数据 ...

  6. WebPack基本概念以及基本使用

    入门Webpack,看这篇就够了 2017年9月18日更新,添加了一个使用webpack配置多页应用的demo,可以点击此处查看 2017年8月13日更新,本文依据webpack3.5.3将文章涉及代 ...

  7. _itemmod_strengthen_item

    `enchant_id`升级后的附魔Id `prev_enchant_id` 上级附魔Id `description` 描述,出现在菜单中 `enchantReqId`升级附魔效果的消耗模板 `rem ...

  8. unity shader base pass and additional pass

      [Unity Shaders]Shader中的光照,shadersshader 写在前面 自己写过Vertex & Fragment Shader的童鞋,大概都会对Unity的光照痛恨不已 ...

  9. Java中的单实例

    前几天刚学完单实例设计模式,今天看代码时发现一行代码很奇怪,getRuntime()函数的返回类型怎么是它本身,忽然想起前几天学的单实例模式,于是找到方法的定义,果然是静态私有变量,获取实例的公有方法 ...

  10. 学习笔记37—WIN7系统本地连接没有有效的IP地址 电脑本地连接无有效ip配置怎么办

    WIN7系统本地连接没有有效的IP地址 电脑本地连接无有效ip配置怎么办 家中有两台笔记本都有无线网卡,现在想让两台笔记本都能够上网,而又不想购买路由器,交换机等设备,这个时候怎么办呢? 其实只要进行 ...