通过递归函数实现多父级求和

问题来自论坛:这样的SQL如何实现
解题来自坛主:唐诗300首

在数据库设计中,经常会设计有多层父子关系的一张表;

此时,可以通过递归函数查询出子级上的所有层级的父级(或父级下所有的子级);

然后再根据所有子级求和操作,得到父级下所有的子级求和。

举例说明(例子来自于论坛这样的需求SQL如何实现

第一步:准备测试数据

CREATE TABLE TAccount(AccountId varchar(50),AccountName varchar(50),ParentAccountId varchar(50))
INSERT INTO TAccount
SELECT '1002','银行存款','' UNION ALL
SELECT '100201','A银行','1002' UNION ALL
SELECT '100202','B银行','1002' UNION ALL
SELECT '100203','C银行','1002' UNION ALL
SELECT '10020301','C银行','100203' 

--表B科目余额表
CREATE TABLE AccountBalance(AccountId varchar(50),Year int,Month int,Amount numeric(19,9))
INSERT INTO AccountBalance
SELECT '100201',2020,1,100 UNION ALL
SELECT '100202',2020,1,300 UNION ALL
SELECT '10020301',2020,1,0 UNION ALL
SELECT '100201',2020,2,150 UNION ALL
SELECT '100202',2020,2,800 UNION ALL
SELECT '10020301',2020,2,500 

第二步:通过递归函数获取层级关系;(查询出子级对应的所有层级的父级)

create FUNCTION [dbo].[getchild]
(	
	@paccount varchar(20)
)
RETURNS TABLE 
AS
RETURN 
(
with t1 as (
select AccountId from TAccount as a where AccountId=@paccount
union all 
select a.AccountId from TAccount as a inner join t1 as b on a.ParentAccountId=b.AccountId
)select * from t1
)

第三步:连接查询,父级ID传入函数(查询父级下所有子级的和)

select 科目代码=a.AccountId,=b.Year,=b.Month,
       期末余额=cast(t.Amount as int)
 from TAccount a
 cross join (select distinct Year,Month from AccountBalance) b
 outer apply(select Amount=sum(x.Amount)
             from AccountBalance x 
             where x.Year=b.Year
             and x.Month=b.Month
             and x.AccountId in(select * from getchild(a.AccountId))) t
			 order by,,科目代码

/*科目代码	年	月	期末余额
1002	2020	1	400
100201	2020	1	100
100202	2020	1	300
100203	2020	1	0
10020301	2020	1	0
1002	2020	2	1450
100201	2020	2	150
100202	2020	2	800
100203	2020	2	500
10020301	2020	2	500
*/

思路二:CTE中直接递归查询出子级所有层级的父级,且每个父+子一条记录。实现代码如下:

;with tr as
(select pid=x.AccountId,sid=y.AccountId
  from (select distinct AccountId from TAccount
        union 
        select distinct ParentAccountId from TAccount) x
  left join TAccount y on x.AccountId=y.ParentAccountId
  where x.AccountId<>''
 union all
 select pid=a.pid,sid=b.AccountId
  from tr a
  inner join TAccount b on a.sid=b.ParentAccountId)
select 科目代码=a.AccountId,=b.Year,=b.Month,
       期末余额=cast(isnull(t.Amount,0) as int)
 from TAccount a
 cross join (select distinct Year,Month from AccountBalance) b
 outer apply(select Amount=sum(x.Amount)
             from AccountBalance x 
             where x.Year=b.Year
             and x.Month=b.Month
             and x.AccountId in(select sid=a.AccountId
                                union 
                                select sid from tr where pid=a.AccountId and sid is not null)) t

/*
科目代码                 年           月           期末余额
-------------------- ----------- ----------- -----------
1002                 2020        1           400
100201               2020        1           100
100202               2020        1           300
100203               2020        1           0
10020301             2020        1           0
1002                 2020        2           1450
100201               2020        2           150
100202               2020        2           800
100203               2020        2           500
10020301             2020        2           500

(10 行受影响)
*/

问题来自论坛:这样的SQL如何实现
解题来自坛主:唐诗300首

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值