[英]How to calculate running balance using PostgreSQL window functions?
I want a query to track interest on a mortgage account.我想要一个查询来跟踪抵押账户的利息。 For simplicity assume the interest is calculated yearly.
为简单起见,假设利息是每年计算的。 There are also one-off deposits/withdrawals (repayments etc).
还有一次性存款/取款(还款等)。
I want to query this information and calculate a running balance, presumably using window functions.我想查询此信息并计算运行余额,大概使用 window 函数。 Here is an example of the kind of table I want to query.
这是我要查询的那种表的示例。
year | changes | interest | comment
2020 | 10000 | 2.5 | initial mortgage of 10k
2021 | 0 | 2.0 | next year the rate drops
2022 | 5000 | 2.0 | we borrow an extra 5k
2023 | 0. | 1.5 | rate drop again
I want a query that calculates the running balance each year, like so:我想要一个计算每年运行余额的查询,如下所示:
year | changes | interest | balance
2020 | 10000 | 2.5 | 10250.0 = 10000 * (1 + 2.5 / 100)
2021 | 0 | 2.0 | 10455.0 = 10250 * (1 + 2.0 / 100)
2022 | 5000 | 2.0 | 15764.1 = (10455 + 5000) * (1 + 2.0 / 100)
2023 | 0. | 1.5 | 16000.56 = 15764.1 * (1 + 1.5 / 100)
How to do this in PostgreSQL?如何在 PostgreSQL 中执行此操作?
Because of the need to multiply the prior year balance by the current interest, this is most easily achieved using a recursive CTE:由于需要将上一年余额乘以当前利息,因此使用递归 CTE 最容易实现:
WITH RECURSIVE CTE AS (
SELECT t.year, t.changes, t.interest, t.changes * (1.0 + t.interest / 100.0) AS balance
FROM transactions t
WHERE year = (SELECT MIN(year) FROM transactions)
UNION ALL
SELECT t.year, t.changes, t.interest, (t.changes + CTE.balance) * (1.0 + t.interest / 100.0)
FROM transactions t
JOIN CTE ON t.year = CTE.year + 1
)
SELECT year, changes, interest, ROUND(CAST(balance AS numeric), 2) AS balance
FROM CTE
Output: Output:
year changes interest balance
2020 10000 2.5 10250.00
2021 0 2 10455.00
2022 5000 2 15764.10
2023 0 1.5 16000.56
The recursive CTE is quite possibly the better approach.递归 CTE 很可能是更好的方法。 But it is possible to do this using window functions.
但是可以使用 window 函数来做到这一点。
The three key ideas are:三个关键思想是:
exp(sum(ln()))
as the product()
aggregation function.exp(sum(ln()))
作为product()
聚合 function。 The actual code is not that complicated:实际的代码并不复杂:
select t.*,
(sum(changes * running_interest) over (order by year) /
coalesce(prev_running_interest, 1)
) as val
from (select t.*,
exp(sum(ln(1 + interest / 100)) over (order by year desc)) as running_interest,
exp(sum(ln(1 + interest / 100)) over (order by year desc rows between unbounded preceding and 1 preceding)) as prev_running_interest
from t
) t
order by year;
You will notice in the db<>fiddle the slight inaccuracies caused by floating point arithmetic.您会在db<>fiddle中注意到浮点运算引起的轻微误差。 You can always cast to fewer decimal places for more aesthetically appealing numbers.
您始终可以减少小数位数以获得更具美感的数字。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.