一条SQL实现数据分摊

这个也是比较经典的数据分摊的问题。
举个例子:
例如有一个总金额:5000
按照一定的顺序分摊给一堆明细记录:
RN        DEPTNO        EMPNO        SAL
1        20         7369         800
2        20        7566         2975
3        20        7788         3000
4        20        7876         1100
5        20        7902         3000
-------
SQL为:
SELECT ROW_NUMBER() OVER (PARTITION BY DEPTNO ORDER BY EMPNO) RN
            ,DEPTNO,EMPNO,SAL
  FROM SCOTT.EMP
WHERE DEPTNO = 20
ORDER BY 1
----
第一笔金额 800 ,分摊800,剩下5000-800=4200
第二笔金额2975 ,分摊2975,剩下:4200-2975=1225待下面继续分摊
第三笔金额3000,只需要分摊1225已经足够。至此,5000全部分摊完毕,退出---
有2种情况必须要考虑:
1 所分摊的明细金额行可能是负数。例如上面例子的,第三行的SAL可能是-3000
2 总金额可能超过所有分摊明细的金额的汇总。
----
目前想到的是用分析函数来实现。
WITH SAL_TEMP AS (SELECT ROW_NUMBER() OVER (PARTITION BY DEPTNO ORDER BY EMPNO) RN
            ,DEPTNO,EMPNO,SAL
            ,SUM(SAL) OVER (PARTITION BY DEPTNO ORDER BY EMPNO) ROLL_DEPT
            ,SUM(SAL) OVER (PARTITION BY DEPTNO) SUM_DEPT
             FROM SCOTT.EMP)
SELECT TEMP.RN
,TEMP.DEPTNO
,TEMP.EMPNO
,TEMP.SAL
,TEMP.ROLL_DEPT
,:LEVEL_SAL
,:LEVEL_SAL-TEMP.ROLL_DEPT LEFT_SAL ---分摊之后总剩余的工资
,:LEVEL_SAL-TEMP.ROLL_DEPT+SAL PRE_LEFT_SAL---总剩余的工资加上本次分摊的工资=上次分摊后剩余的工资。
,LEAST(SAL,(:LEVEL_SAL-TEMP.ROLL_DEPT+SAL)) ALLO_QUANTITY ---分摊的数量 
FROM SAL_TEMP TEMP
WHERE TEMP.DEPTNO = 20
AND (  RN <= (SELECT MIN(RN) FROM SAL_TEMP TEMP2 WHERE TEMP2.DEPTNO = 20 AND TEMP2.ROLL_DEPT >=:LEVEL_SAL)
    OR SUM_DEPT <= :LEVEL_SAL
    )
--AND (:LEVEL_SAL-TEMP.SUM_DEPT+SAL) >0
UNION ALL---超出的部分要做一笔虚拟的记录。
SELECT NULL 
,TEMP.DEPTNO DEPTNO
,0 EMPNO
,:LEVEL_SAL - SUM_DEPT SAL
,:LEVEL_SAL SUM_DEPT
,:LEVEL_SAL LEVEL_SAL
,0 LEFT_SAL
,:LEVEL_SAL - SUM_DEPT PRE_LEFT_SAL
,:LEVEL_SAL - SUM_DEPT ALLO_QUANTITY
FROM SAL_TEMP  TEMP
WHERE :LEVEL_SAL - SUM_DEPT >0
AND TEMP.DEPTNO = 20
AND RN = 1;
---例如要分摊5000:
5000的结果.jpg 
---例如要分摊50000:
50000的结果.jpg 
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值