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

其中,需要做的处理包括

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. 6、nginx的反向代理及缓存功能

    nginx模块的应用 ngx_http_proxy_module  nginx 反向代理模块: http://nginx.org/en/docs/http/ngx_http_proxy_module. ...

  2. .Net ASP.NET 打开指定文件夹

    比如要打开指定的文件夹,而不是弹出对话框 System.Diagnostics.Process.Start(@"D:\"); 这样就打开了D盘,和正常打开D盘是一样的.

  3. Model中时间格式化

    MVC 中 @Html中的时间格式化 @Html.TextBoxFor(model => model.StartTime, "{0:yyyy-MM-dd HH:mm:ss}" ...

  4. Excel 导出通用类

    public class ExportToExcelHelper { public static void ExportExcel(DataTable dt) { try { //创建一个工作簿 IW ...

  5. vue运行报错--dependency

    ERROR Failed to compile with 1 errors 11:17:27 This dependency was not found: 解决方法:把报错所缺少的依赖都装上 如 xx ...

  6. FreeCodeCamp---advanced Algorithm Scripting解法

    Exact Change 设计一个收银程序 checkCashRegister() ,其把购买价格(price)作为第一个参数 , 付款金额 (cash)作为第二个参数, 和收银机中零钱 (cid) ...

  7. django自定制Admin

    如果只是在admin中简单的展示及管理模型,那么在admin.py模块中使用admin.site.register将模型注册一下就好了: from django.contrib import admi ...

  8. VC.【转】采用_beginthread/_beginthreadex函数创建多线程

    https://blog.csdn.net/cbnotes/article/details/8331632 还可以看这个网址的内容:[多线程]VC6使用_beginthread开启多线程的方法-技术宅 ...

  9. 关于使用python.numpy的tips

    产生含有5个数字的随机向量时,注意写法 import numpy as np A=np.random.randn(5,1)   # 注意不要只写5  B=np.random.randn(5)与A不一样 ...

  10. Android打开相机进行人脸识别,使用虹软人脸识别引擎

    上一张效果图,渣画质,能看就好 功能说明: 人脸识别使用的是虹软的FreeSDK,包含人脸追踪,人脸检测,人脸识别,年龄.性别检测功能,其中本demo只使用了FT和FR(人脸追踪和人脸识别),封装了开 ...