甲骨文递归计算税收总额

Oracle recursively calculate total base on tax

我的温度 table 是这样的:

id  d           tax_rate    money
1   20210101    5           100
1   20210201    15          0   
1   20210301    20          0   
1   20210401    5           0 

这是我想要的输出 select:

id  d           tax_rate    money   total
1   20210101    5           100     105
1   20210201    15          105     120.75
1   20210301    20          120.75  144.9
1   20210401    5           144.9   152.145

这意味着我需要根据 tax_rateprevious total 递归计算 total(前一天的总计 = 金钱)。 total = previous total (by date) * (1 + tax_rate)(tax_rate 百分比)

我试过使用 LAG() OVER()LAG 只计算以前的,而不是递归的所以从第 3 天开始计算 return 错误的总数。

在我的例子中,如果我可以使用 LAG 或任何函数来乘以所有先前的 tax_rate(例如 1.05 * 1.15 * 1.2 = 1.449),那么我可以计算出正确的 previous total,但是没有找到一个函数来做到这一点。

WITH tmp AS 
(
    SELECT 1 AS id, 20210101 AS d, 5 AS tax_rate, 1000 AS money FROM dual UNION ALL
    SELECT 1 AS id, 20210201 AS d, 15 AS tax_rate, 0 AS money FROM dual UNION ALL
    SELECT 1 AS id, 20210301 AS d, 20 AS tax_rate, 0 AS money FROM dual UNION ALL
    SELECT 1 AS id, 20210401 AS d, 5 AS tax_rate, 0 AS money FROM dual 
)
SELECT * 
FROM tmp;

一个选项是这样的

WITH tmp AS 
(
    SELECT 1 AS id, 20210101 AS d, 5 AS tax_rate, 100 AS money FROM dual UNION ALL
    SELECT 1 AS id, 20210201 AS d, 15 AS tax_rate, 0 AS money FROM dual UNION ALL
    SELECT 1 AS id, 20210301 AS d, 20 AS tax_rate, 0 AS money FROM dual UNION ALL
    SELECT 1 AS id, 20210401 AS d, 5 AS tax_rate, 0 AS money FROM dual 
),
running_total( id, d, tax_rate, money, total )
as (
  select id, d, tax_rate, money, money * (1 + tax_rate/100) total
    from tmp
   where money != 0
  union all
  select t.id, t.d, t.tax_rate, t.money, rt.total * (1 + t.tax_rate/100)
    from tmp t
         join running_total rt
           on t.id = rt.id
          and to_date( rt.d, 'yyyyddmm' ) = to_date( t.d, 'yyyyddmm' ) - 1
)
select *
  from running_total;

this dbfiddle

我假设构成递归 CTE 基础的第一行是 money != 0 所在的行(因此每个 id 只有一个这样的行)。您可以更改它以根据 id 或您的实际数据支持的任何其他“第一行”逻辑选择最早日期的行。

请注意,如果您使用实际日期作为日期而不是使用代表日期的数字,您的生活会更轻松。对于 4 行虚拟 table,您必须在 running_total 递归 CTE 中的连接两侧执行 to_date 并不重要。但是对于具有相当数量行的真实 table,您希望能够在 (id, d) 上建立索引以获得良好的性能。当然,您可以创建一个基于函数的索引,但是您要么需要在 to_date 调用中明确指定诸如 NLS 环境之类的东西,要么处理会话在 NLS 情况下不使用您的索引的可能性环境与用于创建索引的 NLS 设置不匹配。

你可以尝试用数学公式做乘法累加

然后乘法累加算钱

查询 1:

SELECT  ID, D, tax_rate, 
        SUM(money) OVER(PARTITION BY ID ORDER BY ID) * EXP(SUM(LN(CAST(tax_rate AS DECIMAL(5,2))/100 + 1))over(PARTITION BY ID ORDER BY d)) total
FROM tmp

Results:

| ID |        D | TAX_RATE |   TOTAL |
|----|----------|----------|---------|
|  1 | 20210101 |        5 |     105 |
|  1 | 20210201 |       15 |  120.75 |
|  1 | 20210301 |       20 |   144.9 |
|  1 | 20210401 |        5 | 152.145 |