我正在使用postgreSQL 9.2 我有一个表,其中包含影响帐户启动余额的交易,我想创建一个生成月度余额的查询。看起来很简单,但是我被卡住了,我能找到的最接近的东西可能就是这个Calculate Monthly Recurring Revenue(MRR) result using postgres 因为它使用generate_series,我认为我需要创建一个月份列表来匹配我的帐户和交易。
这是我桌子的DDL:
CREATE TABLE account (
id integer NOT NULL,
startbalance numeric(19,2),
opendate date NOT NULL
);
CREATE TABLE trx (
id integer NOT NULL,
accountid integer NOT NULL,
amount numeric(19,2) NOT NULL,
transactiondate date NOT NULL
);
ALTER TABLE ONLY account
ADD CONSTRAINT account_pk PRIMARY KEY (id);
ALTER TABLE ONLY trx
ADD CONSTRAINT transaction_pk PRIMARY KEY (id);
ALTER TABLE ONLY trx
ADD CONSTRAINT transaction_accountid_fk FOREIGN KEY (accountid) REFERENCES account(id);
数据:
INSERT INTO account VALUES (1, 200.00, '2016-01-01');
INSERT INTO account VALUES (2, 400.00, '2016-03-02');
INSERT INTO account VALUES (3, 800.00, '2016-01-15');
INSERT INTO trx VALUES (1, 1, -100.00, '2016-01-10');
INSERT INTO trx VALUES (6, 2, -200.00, '2016-03-25');
INSERT INTO trx VALUES (9, 3, -400.00, '2016-02-03');
INSERT INTO trx VALUES (8, 2, -100.00, '2016-09-15');
INSERT INTO trx VALUES (5, 1, -20.00, '2016-06-20');
INSERT INTO trx VALUES (3, 1, -20.00, '2016-03-04');
INSERT INTO trx VALUES (2, 1, -10.00, '2016-02-02');
INSERT INTO trx VALUES (10, 3, -100.00, '2016-04-12');
INSERT INTO trx VALUES (11, 3, -100.00, '2016-04-25');
INSERT INTO trx VALUES (12, 3, -200.00, '2016-04-29');
INSERT INTO trx VALUES (4, 1, -50.00, '2016-06-05');
INSERT INTO trx VALUES (7, 2, -100.00, '2016-08-05');
我已经弄清楚如何使用窗口函数来获取每笔交易的总计:
SELECT
acct.id accountid,
acct.startbalance,
amount,
transactiondate,
acct.startbalance + sum(amount) OVER (PARTITION BY acct.id ORDER BY transactiondate) balance
FROM
account acct
LEFT JOIN trx ON trx.accountid = acct.id
ORDER BY transactiondate DESC
这给了我:
accountid opendate startbalance amount transactiondate balance
2 2016-03-02 400.00 -100.00 2016-09-15 0.00
2 2016-03-02 400.00 -100.00 2016-08-05 100.00
1 2016-01-01 200.00 -20.00 2016-06-20 0.00
1 2016-01-01 200.00 -50.00 2016-06-05 20.00
3 2016-01-15 800.00 -200.00 2016-04-29 0.00
3 2016-01-15 800.00 -100.00 2016-04-25 200.00
3 2016-01-15 800.00 -100.00 2016-04-12 300.00
2 2016-03-02 400.00 -200.00 2016-03-25 200.00
1 2016-01-01 200.00 -20.00 2016-03-04 70.00
3 2016-01-15 800.00 -400.00 2016-02-03 400.00
1 2016-01-01 200.00 -10.00 2016-02-02 90.00
1 2016-01-01 200.00 -100.00 2016-01-10 100.00
但它没有显示没有数据的月份的任何内容。我需要的是更像结果集的东西,它显示每个月的数据范围(例如1/2016 - 6/2016):
MonthOf AcctId OrigBal Pmt Balance
2016-06 3 800 0 0
2016-06 2 400 0 200
2016-06 1 200 -70 0
2016-05 3 800 0 0
2016-05 2 400 0 200
2016-05 1 200 0 70
2016-04 3 800 -400 0
2016-04 2 400 0 200
2016-04 1 200 0 70
2016-03 3 800 -400 400
2016-03 2 400 -200 200
2016-03 1 200 -20 70
2016-02 3 800 -400 400
2016-02 2 0 0 0
2016-02 1 200 -10 90
2016-01 3 800 0 800
2016-01 2 0 0 0
2016-01 1 200 -100 100
我尝试了使用generate_series语句加入CTE的示例,但无法获得预期的结果。下面是我试图放入cte的generate_series结果。我无法正确添加余额。我是窗口功能的新手,所以我确定我只是缺少一些东西,或者我只是在错误的道路上工作。
SELECT
date_trunc('month', dates) + INTERVAL '1 MONTH - 1 SECOND' end_of_month
FROM
generate_series('2016-01-01'::timestamp, '2016-6-24'::timestamp, '1 month'::interval) dates
答案 0 :(得分:0)
没有窗口功能(可能有点难看,但似乎有用......):
注意:我忽略了account.opendate
,因为它与求和无关。
更新:未出生的帐户在WHILE子句中处理,现在
WITH cal AS ( -- calender table
SELECT generate_series('2016-01-01'::date, '2016-10-01'::date , '1 month'::interval)::date dt
)
SELECT
a.id, a.startbalance
, c.dt
, COALESCE(t.cumsum, 0.0)::NUMERIC(19,2) AS cumsum
, a.startbalance + COALESCE(t.cumsum, 0.0)::NUMERIC(19,2) AS balance
FROM cal c
CROSS JOIN account a
LEFT JOIN LATERAL ( -- aggregating subquery
SELECT accountid
, SUM(amount) AS cumsum
FROM trx
WHERE transactiondate < c.dt
GROUP BY accountid
) t ON t.accountid = a.id
WHERE a.opendate <= c.dt -- suppress unborn accounts
ORDER BY a.id, c.dt
;
更新:我们可以通过普通的老式标量子查询来避免LATERAL
。
WITH cal AS ( -- calender table
SELECT generate_series('2016-01-01', '2016-10-01' , '1 month'::interval)::date dt
)
, xxx AS ( -- grid table
SELECT a.startbalance , a.id AS accountid , c.dt AS xdt
FROM cal c -- in fact a cross join ...
JOIN account a ON a.opendate <= c.dt
)
SELECT
x.accountid, x.startbalance, x.xdt
, (SELECT SUM(t.amount) FROM trx t
WHERE t.accountid = x.accountid
AND t.transactiondate <= x.xdt) AS cumsum
, COALESCE(x.startbalance
+ (SELECT SUM(t.amount) FROM trx t
WHERE t.accountid = x.accountid
AND t.transactiondate <= x.xdt)
, x.startbalance) AS balance
FROM xxx x
ORDER BY x.accountid, x.xdt
;
答案 1 :(得分:0)
这给了我可以使用的结果:
WITH dates AS (
SELECT
date_trunc('month', dates) + INTERVAL '1 MONTH - 1 SECOND' end_of_month
FROM
generate_series('2016-01-01'::timestamp, '2016-6-24'::timestamp, '1 month'::interval) dates
)
SELECT
date_trunc('month', dates.end_of_month) endofmonth,
acct.id accountid,
acct.opendate,
acct.startbalance,
acct.startbalance + (SUM(sum(COALESCE(trx.amount, 0))) OVER (PARTITION BY acct.id ORDER BY dates.end_of_month)) balance
FROM
dates
LEFT join account acct ON date_trunc('month', dates.end_of_month) >= date_trunc('month',acct.opendate)
LEFT JOIN trx ON trx.accountid = acct.id AND date_trunc('month', trx.transactionDate) = date_trunc('month', dates.end_of_month)
GROUP BY
dates.end_of_month,
acct.id,
acct.opendate,
acct.startbalance
ORDER BY endofmonth DESC, acct.id DESC