如何解决Oracle SQL层次结构汇总
我有一个TRANS表,其中包含以下记录:
TRANS_ID TRANS_DT QTY
1 01-Aug-2020 5
1 01-Aug-2020 1
1 03-Aug-2020 2
2 02-Aug-2020 1
预期输出:
TRANS_ID TRANS_DT BEGBAL TOTAL END_BAL
1 01-Aug-2020 0 6 6
1 02-Aug-2020 6 0 6
1 03-Aug-2020 6 2 8
2 01-Aug-2020 0 0 0
2 02-Aug-2020 0 1 1
2 03-Aug-2020 1 0 1
每个trans_id的起始余额为0(2020年8月1日)。对于后续的几天,期初余额是前一天的期末余额,依此类推。 我可以创建PL / SQL块来创建输出。是否可以在1条SQL语句中获得输出?
谢谢。
解决方法
使用CTE尝试以下脚本-
WITH CTE
AS
(
SELECT DISTINCT A.TRANS_ID,B.TRANS_DT
FROM your_table A
CROSS JOIN (SELECT DISTINCT TRANS_DT FROM your_table) B
),CTE2
AS
(
SELECT C.TRANS_ID,C.TRANS_DT,SUM(D.QTY) QTY
FROM CTE C
LEFT JOIN your_table D
ON C.TRANS_ID = D.TRANS_ID
AND C.TRANS_DT = D.TRANS_DT
GROUP BY C.TRANS_ID,C.TRANS_DT
ORDER BY C.TRANS_ID,C.TRANS_DT
)
SELECT F.TRANS_ID,F.TRANS_DT,(
SELECT COALESCE (SUM(QTY),0) FROM CTE2 E
WHERE E.TRANS_ID = F.TRANS_ID AND E.TRANS_DT < F.TRANS_DT
) BEGBAL,0) FROM CTE2 E
WHERE E.TRANS_ID = F.TRANS_ID AND E.TRANS_DT = F.TRANS_DT
) TOTAL,0) FROM CTE2 E
WHERE E.TRANS_ID = F.TRANS_ID AND E.TRANS_DT <= F.TRANS_DT
) END_BAL
FROM CTE2 F
,
您也可以这样做(我想它会快一点):Demo
with
dt_between as (
select mindt + level - 1 as trans_dt
from (select min(trans_dt) as mindt,max(trans_dt) as maxdt from t)
connect by level <= maxdt - mindt + 1
),dt_for_trans_id as (
select *
from dt_between,(select distinct trans_id from t)
),qty_change as (
select distinct trans_id,trans_dt,sum(qty) over (partition by trans_id,trans_dt) as total,sum(qty) over (partition by trans_id order by trans_dt) as end_bal
from t
right outer join dt_for_trans_id using (trans_id,trans_dt)
)
select
trans_id,to_char(trans_dt,'DD-Mon-YYYY') as trans_dt,nvl(lag(end_bal) over (partition by trans_id order by trans_dt),0) as beg_bal,nvl(total,0) as total,nvl(end_bal,0) as end_bal
from qty_change q
order by trans_id,trans_dt
dt_between
返回数据中min(trans_dt)
和max(trans_dt)
之间的所有日期。
dt_for_trans_id
将为您数据中的每个trans_id
返回所有这些天。
qty_change
会发现每天的差异(在您的示例中为TOTAL
)和所有天的累计金额(在示例中为END_BAL
)。
主选择从前一天取END_BAL
并命名为BEG_BAL
,它还会对最终输出进行一些格式化。
首先,您需要生成日期,然后需要通过TRANS_DT汇总您的值,然后使汇总数据与日期保持连接。获得所需总和的最简单方法是使用分析窗函数:
with dates(dt) as ( -- generating dates between min(TRANS_DT) and max(TRANS_DT) from TRANS
select min(trans_dt) from trans
union all
select dt+1 from dates
where dt+1<=(select max(trans_dt) from trans)
),trans_agg as ( -- aggregating QTY in TRANS
select TRANS_ID,TRANS_DT,sum(QTY) as QTY
from trans
group by TRANS_ID,TRANS_DT
)
select -- using left join partition by to get data on daily basis for each trans_id:
dt,trans_id,nvl(sum(qty) over(partition by trans_id order by dates.dt range between unbounded preceding and 1 preceding),0) as BEGBAL,nvl(qty,0) as TOTAL,nvl(sum(qty) over(partition by trans_id order by dates.dt),0) as END_BAL
from dates
left join trans_agg tr
partition by (trans_id)
on tr.trans_dt=dates.dt;
带有示例数据的完整示例:
alter session set nls_date_format='dd-mon-yyyy';
with trans(TRANS_ID,QTY) as (
select 1,to_date('01-Aug-2020'),5 from dual union all
select 1,1 from dual union all
select 1,to_date('03-Aug-2020'),2 from dual union all
select 2,to_date('02-Aug-2020'),1 from dual
),dates(dt) as ( -- generating dates between min(TRANS_DT) and max(TRANS_DT) from TRANS
select min(trans_dt) from trans
union all
select dt+1 from dates
where dt+1<=(select max(trans_dt) from trans)
),TRANS_DT
)
select
dt,0) as END_BAL
from dates
left join trans_agg tr
partition by (trans_id)
on tr.trans_dt=dates.dt;
,
您可以使用递归查询来生成整个日期范围,将其与单独的cross join
列表一起tran_id
,然后将表中带有left join
。最后一步是聚合和窗口函数:
with all_dates (trans_dt,max_dt) as (
select min(trans_dt),max(trans_dt) from trans group by trans_id
union all
select trans_dt + interval '1' day,max_dt from all_dates where trans_dt < max_dt
)
select
i.trans_id,d.trans_dt,coalesce(sum(sum(t.qty)) over(partition by i.trans_id order by d.trans_dt),0) - coalesce(sum(t.qty),0) begbal,coalesce(sum(t.qty),0) total,0) endbal
from all_dates d
cross join (select distinct trans_id from trans) i
left join trans t on t.trans_id = i.trans_id and t.trans_dt = d.trans_dt
group by i.trans_id,d.trans_dt
order by i.trans_id,d.trans_dt
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。