甲骨文递归计算税收总额
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_rate
和 previous 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;
我假设构成递归 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
| ID | D | TAX_RATE | TOTAL |
|----|----------|----------|---------|
| 1 | 20210101 | 5 | 105 |
| 1 | 20210201 | 15 | 120.75 |
| 1 | 20210301 | 20 | 144.9 |
| 1 | 20210401 | 5 | 152.145 |
我的温度 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_rate
和 previous 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;
我假设构成递归 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
| ID | D | TAX_RATE | TOTAL |
|----|----------|----------|---------|
| 1 | 20210101 | 5 | 105 |
| 1 | 20210201 | 15 | 120.75 |
| 1 | 20210301 | 20 | 144.9 |
| 1 | 20210401 | 5 | 152.145 |