PostgreSQL:从当前余额向后计算余额

Sha*_*vil 3 postgresql recursive

我有一张balances表,current_balance用于存储银行账户;以及一个transfers表格,其中包含每次转账到账户和从账户转账的行(存款为正数,取款为负数)。

我需要创建一个帐户摘要,其中包含帐户的每次转账,以及该转账产生的余额。

例子:

+--------------------------+--------+---------+
|      activity_date       | amount | balance |
+--------------------------+--------+---------+
| 2015-12-24T20:27:00.670Z |     10 |     180 |
| 2015-12-19T12:13:50.085Z |   -275 |     170 |
| 2015-12-18T23:56:22.513Z |     10 |     445 |
| 2015-12-18T23:54:46.880Z |     50 |     435 |
| 2015-12-17T03:32:10.707Z |   -120 |     385 |
| 2015-12-12T03:56:50.775Z |     35 |     505 |
| 2015-12-11T23:09:40.211Z |    -20 |     470 |
| 2015-12-03T01:17:59.460Z |    -10 |     490 |
| 2015-11-23T15:39:35.003Z |    500 |     500 |
+--------------------------+--------+---------+
Run Code Online (Sandbox Code Playgroud)

由于我只有该帐户的当前余额,因此我需要从那里开始并返回到给定日期。在电子表格中,我会通过从以前的余额中减去转账金额来计算新余额,但是我无法将其转换为 SQL。

架构

CREATE SCHEMA temp;

CREATE TABLE temp.balances (
  account_id      INT
, current_balance INT
);

CREATE TABLE temp.transfers (
  account_id      INT
, activity_date   TIMESTAMP
, amount          INT
);

INSERT INTO temp.balances (account_id, current_balance)
VALUES (1, 180);

INSERT INTO temp.transfers (account_id, activity_date, amount)
VALUES (1, '2015-12-24T20:27:00.670Z', 10)
, (1, '2015-12-19T12:13:50.085Z', -275)
, (1, '2015-12-18T23:56:22.513Z', 10)
, (1, '2015-12-18T23:54:46.880Z', 50)
, (1, '2015-12-17T03:32:10.707Z', -120)
, (1, '2015-12-12T03:56:50.775Z', 35)
, (1, '2015-12-11T23:09:40.211Z', -20)
, (1, '2015-12-03T01:17:59.460Z', -10)
, (1, '2015-11-23T15:39:35.003Z', 500);
Run Code Online (Sandbox Code Playgroud)

SQL

WITH transfers AS (
  SELECT *
  FROM temp.transfers
  WHERE account_id = 1
    AND activity_date BETWEEN '2015-10-01'::DATE AND CURRENT_TIMESTAMP
  ORDER BY activity_date DESC
)
SELECT
  t.activity_date
, t.amount
, (LAG(balance, 1, b.current_balance) OVER (ORDER BY t.activity_date DESC)::INT) - (LAG(t.amount, 1, 0) OVER (ORDER BY t.activity_date DESC)::INT) AS balance
FROM transfers t
JOIN temp.balances b ON b.account_id = t.account_id
Run Code Online (Sandbox Code Playgroud)

此查询失败,因为我试图访问由窗口函数生成的余额值。我研究了WITH RECURSIVE,这似乎是使用正确的技术,但无法让它发挥作用。我也不确定它是否可以与其他 WITH 子句结合使用,而我需要这样做。

所以我的问题是:在PostgreSQL 9.3 中,如何计算每次转账后的余额,从当前余额向后计算?

更新

使用下面 Julien 答案的稍微修改版本,我尝试了这个:

WITH transfers AS (
  SELECT *
  FROM temp.transfers
  WHERE account_id = 1
    AND activity_date BETWEEN '2015-10-01'::DATE AND CURRENT_TIMESTAMP
)
SELECT
  t.activity_date
, t.amount
, b.current_balance - SUM(t.amount) OVER(ORDER BY t.activity_date DESC) AS balance
FROM transfers t
JOIN temp.balances b ON b.account_id = t.account_id
Run Code Online (Sandbox Code Playgroud)

...这几乎是正确的,只是余额向上移动了一行。

Jul*_*eur 6

你确实需要一个窗口函数。然而,滞后不是正确的。SUM(...) OVER(...)是你想要的。请参阅SQL 小提琴

询问:

SELECT account_id, activity_date, amount
    , SUM(amount) OVER(PARTITION BY account_id ORDER BY activity_date) as balance
FROM transfers t
ORDER BY account_id, activity_date DESC;
Run Code Online (Sandbox Code Playgroud)

输出:

account_id  | activity_date                 | amount    | balance
1           | 2015-12-24 20:27:00.6700000   | 10        | 180
1           | 2015-12-19 12:13:50.0850000   | -275      | 170
1           | 2015-12-18 23:56:22.5130000   | 10        | 445
1           | 2015-12-18 23:54:46.8800000   | 50        | 435
1           | 2015-12-17 03:32:10.7070000   | -120      | 385
1           | 2015-12-12 03:56:50.7750000   | 35        | 505
1           | 2015-12-11 23:09:40.2110000   | -20       | 470
1           | 2015-12-03 01:17:59.4600000   | -10       | 490
1           | 2015-11-23 15:39:35.0030000   | 500       | 500
Run Code Online (Sandbox Code Playgroud)