Как рассчитать текущий баланс с помощью оконных функций PostgreSQL? - PullRequest
2 голосов
/ 04 мая 2020

Я хочу запрос для отслеживания процентов по ипотечному счету. Для простоты предположим, что проценты начисляются ежегодно. Существуют также разовые депозиты / снятие средств (repayments et c).

Я хочу запросить эту информацию и рассчитать текущий баланс, предположительно с использованием оконных функций. Вот пример таблицы, которую я хочу запросить.

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

Мне нужен запрос, который рассчитывает текущий баланс каждый год, например:

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)

Как это сделать это в PostgreSQL?

Ответы [ 2 ]

2 голосов
/ 04 мая 2020

Рекурсивный CTE, возможно, является лучшим подходом. Но это можно сделать с помощью оконных функций.

Три ключевых идеи:

  • Использование exp(sum(ln())) в качестве функции агрегирования product().
  • Проецирование каждого значения на самое позднее время с накоплением всех функций начисления процентов.
  • Разделение на «накопленный процент» до этого значения для корректировки ввода новых значений.

Фактический код это не так сложно:

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;

В скрипте db <> вы заметите небольшие неточности, вызванные арифметикой с плавающей запятой c. Вы всегда можете привести к меньшему количеству десятичных знаков для более эстетически привлекательных чисел.

2 голосов
/ 04 мая 2020

В связи с необходимостью умножения сальдо предыдущего года на текущий процент, это легче всего сделать с помощью рекурсивного 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

Вывод:

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

Демо на dbfiddle

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...