这个也是比较经典的数据分摊的问题。
举个例子:
例如有一个总金额: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:
---例如要分摊50000:
举个例子:
例如有一个总金额: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:
---例如要分摊50000: