Postgres 银行账户交易余额

时间:2021-05-26 02:05:37

标签: sql postgresql

这是一个示例“交易”表,其中每一行都是一笔金额和交易日期的记录。

+--------+------------+
| amount |    date    |
+--------+------------+
| 1000   | 2020-01-06 |
|  -10   | 2020-01-14 |
|  -75   | 2020-01-20 |
|   -5   | 2020-01-25 |
|   -4   | 2020-01-29 |
|  2000  | 2020-03-10 |
|   -75  | 2020-03-12 |
|   -20  | 2020-03-15 |
|    40  | 2020-03-15 |
|   -50  | 2020-03-17 |
|   200  | 2020-10-10 |
|  -200  | 2020-10-10 |
+--------+------------+

目标是返回一列“余额”,其中包含所有交易的余额。唯一的问题是每个月有 5 美元的月费,并且没有至少三笔支付交易(由金额列中的负值表示)总金额至少为 100 美元。因此,在该示例中,唯一没有 5 美元费用的月份是 3 月,因为有 3 次付款(负金额交易)总计 145 美元。因此,最终余额为 2,746 美元。金额总和为 2,801 美元减去 55 美元的月费(11 个月 X 5)。我无论如何都不是 postgres 专家,所以如果有人有任何关于如何开始解决这个问题的指示,或者 postgres 文档的哪些部分最能帮助我解决这个问题,我将不胜感激。

预期输出为:

+---------+
| balance |
+---------+
|  2746   |
+---------+

2 个答案:

答案 0 :(得分:0)

这个比较复杂。您可以计算总月份跨度,然后减去取消费用的月份:

select amount, (extract(year from age) * 12 + extract(month from age)), cnt,
       amount - 5 *( extract(year from age) * 12 + extract(month from age) + 1 - cnt) as balance
from (select sum(amount) as amount,
             age(max(date), min(date)) as age
      from transactions t 
     ) t cross join
     (select count(*) as cnt
      from (select date_trunc('month', date) as yyyymm, count(*) as cnt, sum(amount) as amount
            from transactions t
            where amount < 0
            group by yyyymm
            having count(*) >= 3 and sum(amount) < -100
           ) tt
     ) tt;

Here 是一个 db<>fiddle。

这会计算出 2756,这似乎符合您的规则。如果您想要全年,您可以使用 12 而不是使用 age() 进行计算。

答案 1 :(得分:0)

我会先用一个 generate_series 表示您感兴趣的月份(在本例中,都是 2020 年)。这增加了余额为 0 的缺失月份。

然后我每月汇总这些值,并加上每月的负余额和负余额的数量。

最后,我计算总计并减去每个月不符合条件的费用。

SELECT sum(amount_per_month) -
       sum(5) FILTER (WHERE negative_per_month > -100 OR negative_count < 3)
FROM (SELECT sum(amount) AS amount_per_month,
             sum(amount) FILTER (WHERE amount < 0) AS negative_per_month,
             month_start,
             count(*) FILTER (WHERE amount < 0) AS negative_count
      FROM (SELECT coalesce(t.amount, 0) AS amount,
                   coalesce(date_trunc('month', CAST (t.day AS timestamp)), dates.d) AS month_start
            FROM generate_series(
                    TIMESTAMP '2020-01-01',
                    TIMESTAMP '2020-12-01',
                    INTERVAL '1 month'
                 ) AS dates (d)
               LEFT JOIN transactions AS t
                  ON dates.d = date_trunc('month', CAST (t.day AS timestamp))
            ) AS gaps_filled
      GROUP BY month_start
     ) AS sums_per_month;