考虑以下两个表:
表A:
PIN | ENCOUNTER | BALANCE | REFERENCE_DATE
------------------------------------------
P1 | ABC | 100 | 11-19-2014
P1 | HJI | 300 | 11-20-2014
P1 | PIY | 700 | 11-21-2014
P2 | CDO | 200 | 11-20-2014
P2 | NHG | 200 | 11-21-2014
P3 | CVB | 500 | 11-20-2014
P3 | SJK | 100 | 11-21-2014
表B:
PIN | DEPOSIT
-------------
P1 | 1000
P2 | 400
P3 | 100
最初,表B
的{{1}}值将从表DEPOSIT
中的BALANCE
中减去,A
最早与REFERENCE_DATE
匹配}}。如果差异大于0,则会从下一行的PIN
中减去差异,直到剩余的BALANCE
变为小于或等于0。
从余额中减去存款后的结果将如下所示。我已经列入了另一个专栏,其中存款按每次遭遇划分:
DEPOSIT
我的Postgres版本是9.3。我正在努力为这一个制定查询。
答案 0 :(得分:2)
DEPOSIT
涵盖BALANCE
正如您澄清的那样,您不希望运行总和BALANCE
,只需设置为0,直到花费DEPOSIT
:
SELECT PIN, ENCOUNTER
, CASE WHEN last_sum >= DEPOSIT THEN BALANCE
ELSE GREATEST (last_sum + BALANCE - DEPOSIT, 0) END AS BALANCE
, REFERENCE_DATE
, CASE WHEN last_sum >= DEPOSIT THEN 0
ELSE LEAST (BALANCE, DEPOSIT - last_sum) END AS DEPOSITS_BREAKDOWN
FROM (
SELECT a.*
, COALESCE(sum(a.BALANCE) OVER (
PARTITION BY PIN ORDER BY a.REFERENCE_DATE
ROWS BETWEEN UNBOUNDED PRECEDING
AND 1 PRECEDING), 0) AS last_sum
, COALESCE(b.DEPOSIT, 0) AS DEPOSIT
FROM table_a a
LEFT JOIN table_b b USING (pin)
) sub;
准确地返回您想要的结果。
我采用了@vyegorov更简单的连接的想法作为评论。
LEFT JOIN
到table_b
- 这样就有可能找不到table_b
中的行。
在子查询中,计算BALANCE
到最后一行(last_sum
)的运行总和。在窗口功能中使用自定义框架。并且COALESCE
默认为0,其中没有行。相关答案以及自定义框架的更多说明:
在最终SELECT
中,如果BALANCE
等于或大于last_sum
(已花费),则返回原始DEPOSIT
。 ELSE返回剩余的差异,或0表示BALANCE
(last_sum + BALANCE
)的运行总和小于DEPOSIT
。
上一个(更简单)回答BALANCE
作为运行总和(最后一行500而不是100):
SELECT a.PIN, a.ENCOUNTER
, GREATEST(sum(a.BALANCE) OVER (PARTITION BY PIN ORDER BY a.REFERENCE_DATE)
- COALESCE(b.DEPOSIT, 0), 0) AS BALANCE
, a.REFERENCE_DATE
FROM table_a a
LEFT JOIN table_b b USING (pin);
答案 1 :(得分:1)
我提出了这个问题:
SELECT *,
sum(balance) OVER w balance_accum,
greatest(deposit - sum(balance) OVER w, 0) deposit_new,
greatest(sum(balance) OVER w - deposit, 0) balance_new
FROM table_a JOIN table_b USING(pin)
WINDOW w AS (PARTITION BY pin ORDER BY reference_date)
ORDER BY pin, reference_date;
的 SQL Fiddle 强>
正如欧文所提到的,这个假设最后一行包含500
而不是100
。
修改强>:
此查询产生所需的输出:
SELECT s.*,
CASE WHEN min(deposit_new) OVER w = 0 THEN 0
ELSE least(min(deposit_new) OVER w, deposit_diff) END deposit_used,
balance -
CASE WHEN min(deposit_new) OVER w = 0 THEN 0
ELSE least(min(deposit_new) OVER w, deposit_diff) END balance_real
FROM
(
SELECT *,
sum(balance) OVER w balance_accum,
greatest(coalesce(deposit,0) - sum(balance) OVER w, 0) deposit_new,
least(balance, coalesce(deposit,0)) deposit_diff
FROM table_a LEFT JOIN table_b USING(pin)
WINDOW w AS (PARTITION BY pin ORDER BY reference_date)
) s
WINDOW w AS (PARTITION BY pin ORDER BY reference_date
ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING)
ORDER BY pin, reference_date;
这里有什么(子查询):
LEFT JOIN
和coalesce(deposit,0)
用于保留没有存款的条目; balance
值的总计,并从deposit
(输出中的列deposit_new
)中减去; deposit_diff
至少是balance
和deposit
,用于调整外部的余额。在外部:
deposit_new
值,如果达到0
,则进一步使用"用法"被跳过; deposit_new
和deposit_diff
个值; 我必须使用子查询才能在逻辑中使用窗口函数的结果。
的 SQL Fiddle 2 强>