假设我有一个名为transaction的表,其中包含transaction_date,deposit,withdraw字段。一天可能有也可能没有交易,但每天可以有多笔交易。所以,我需要做的是给出一个日期范围,比如说2010年12月1日到2010年12月31日,我需要计算出每天的最低余额。我们假设在2010年12月1日之前也有交易。是否有人可以就此问题给我一个想法?
谢谢。
更新 使用示例
tran_date withdraw deposit
2010-11-23 0.00 50.00
2010-12-10 0.00 50.00
2010-12-10 0.00 200.00
2010-12-12 100.00 0.00
2010-12-20 0.00 50.00
2010-12-20 70.00 0.00
2010-12-20 0.00 50.00
2010-12-20 0.00 50.00
2010-12-24 150.00 0.00
在上面的示例中,从 Dec 1 到 Dec 10 的最低每日余额将 50 。在 12月10日,有两个存款总计 70 ,但当天的最低余额将 50 (从前一天结转)。
现在让我们看看多个交易。
Dec 20 的结转 200 。第一笔存款使其 250 ,第二笔存款使其 180 ,第三笔存款使其 230 ,最后一笔交易使其 280 即可。因此,在当天的第二笔交易中撤销 70 后,当天的最低余额将 180 。是否可以使用PostgreSQL 8.4上的查询生成它,还是应该使用其他方法?
答案 0 :(得分:2)
<强> EDIT2 强>
这是一个完整的例子,包括前一天的(最小)余额(据我所知,这么小的一组数据)。它应该在8.4上运行。
我重构派生表以使用CTE(公用表表达式)使它(希望)更具可读性:
WITH days AS ( -- generate a liste of possible dates spanning -- the whole interval of the transactions SELECT min(tran_date) + generate_series(0, max(tran_date) - min(tran_date)) AS some_date FROM transaction ), total_balance AS ( -- Calculate the running totals for all transactions SELECT tran_id, days.some_date as tran_date, deposit, withdrawal, sum(deposit - withdrawal) OVER (ORDER BY some_date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) as balance FROM days LEFT JOIN transaction t ON t.tran_date = days.some_date ), min_balance AS ( -- calculate the minimum balance for each day -- (the smalles balance will have a '1' in the column balance_rank) SELECT tran_id, tran_date, rank() OVER (PARTITION BY tran_date ORDER BY balance) as balance_rank, balance FROM total_balance ) -- Now get everything, including the balance for the previous day SELECT tran_id, tran_date, balance, lag(balance) over (order by tran_date) as previous_balance FROM min_balance WHERE balance_rank = 1;
答案 1 :(得分:2)
忽略其他答案中的所有内容。那个家伙Malvolio是个傻瓜,也是个白痴。试试这个:
SELECT MIN(balance), transaction_date FROM
( SELECT a.transaction_date, IFNULL(sum(b.deposit) - sum(b.withdrawal), 0) balance FROM transaction a
LEFT JOIN transaction b ON a.seqno > b.seqno GROUP ON a.seqno
UNION
SELECT a.transaction_date, IFNULL(sum(b.deposit) - sum(b.withdrawal), 0) balance FROM transaction a
LEFT JOIN transaction b ON a.seqno >= b.seqno GROUP ON a.seqno ) x
GROUP BY transaction_date;
当我发生这种情况时,我正要睡着了。 IFNULL可能是MySQL特有的东西,但你可以找到Postgres等价物。
答案 2 :(得分:0)
我假设你说的最低限度是你在一天的开始或结束时少开的?
我想每天你都会做这样的事情:
前一天的平衡:
SELECT (SUM(deposit) - SUM(withdrawal)) WHERE date < [date you're after]
(不确定如何在PostgreSQL中进行日期比较
然后:
SELECT (SUM(deposit) - SUM(withdrawal)) WHERE date = [date you're after]
然后以较大者为准。
如果那不是您的意思,我们需要更多信息。
答案 3 :(得分:0)
首先,我假设交易是按顺序编号。根据定义,交易必须正确订购(因为50美元的存款,然后在同一天提取50美元,将以不同的顺序从相同的步骤产生非常不同的答案),并按顺序编号使其他事情变得更容易。然后我们必须做一些程序性的手工操作:
CREATE TABLE running_total (seqno INT, transaction_date DATE, before NUMBER(10,20), after NUMBER(10,20);
SET tot=0;
FOR transaction IN SELECT * FROM transaction ORDER BY seqno ASC LOOP
SET oldtot = tot;
SET tot = tot = transaction.deposit - transaction.withdrawal;
EXECUTE 'INSERT INTO running_total (seqno, transaction_date, before, after) VALUES (' ||
transaction.seqno || ', ' || transaction.transaction_date || ',' || oldtot || ',' || tot || ')';
END LOOP;
(原谅任何错别字 - 我没有PostGres方便)。现在我们有一张包含所有余额的表格,我们只需要挖掘它。
SELECT MIN(balance), transaction_date FROM
( SELECT before as balance, transaction_date FROM running_total
UNION
SELECT after as balance, transaction_date FROM running_total) x
GROUP BY transaction_date;
我不能在这里测试,但它应该有用。
答案 4 :(得分:0)
假设您在一天内为您的交易编号,我采用了以下架构:
CREATE TABLE transaction (
tran_date date,
num int,
withdraw numeric,
deposit numeric
);
INSERT INTO transaction VALUES
('2010-11-23', 1, 0.00, 50.00),
('2010-12-10', 1, 0.00, 50.00),
('2010-12-10', 2, 0.00, 200.00),
('2010-12-12', 1, 100.00, 0.00),
('2010-12-20', 1, 0.00, 50.00),
('2010-12-20', 2, 70.00, 0.00),
('2010-12-20', 3, 0.00, 50.00),
('2010-12-20', 4, 0.00, 50.00),
('2010-12-24', 1, 150.00, 0.00);
然后,以下查询会显示您的答案:
WITH dates (tran_date) AS (SELECT date '2010-12-01' + generate_series(0, 30)),
transactions AS (SELECT tran_date, num,
coalesce(withdraw, 0) AS withdraw,
coalesce(deposit, 0) AS deposit
FROM dates FULL OUTER JOIN transaction USING (tran_date)),
running_totals AS (SELECT tran_date,
sum(deposit - withdraw) OVER (ORDER BY tran_date, num ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING) AS lagging_total,
sum(deposit - withdraw) OVER (ORDER BY tran_date, num ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS current_total
FROM transactions)
SELECT tran_date, min(least(lagging_total, current_total))
FROM running_totals
GROUP BY tran_date
HAVING tran_date IN (SELECT tran_date FROM dates)
ORDER BY tran_date;
但请注意,您需要PostgreSQL 9.0,因为早期版本不支持1 PRECEDING
子句。如果你无法升级,你可能需要某种程序解决方案,如其他答案所示。
无论如何,我建议为此编写单元测试。 ; - )
答案 5 :(得分:0)
为什么不在跟踪当前余额的数据库中添加一列(在每次存款/取款时计算)。这样,只需要在您感兴趣的日期范围内返回该列的最小值。
答案 6 :(得分:0)
谢谢大家的帮助。我用以下方法来解决这个问题。我不知道代码的效率如何。
select dt::date,
coalesce(case when balance<=coAmt then balance else coAmt end,
(select sum(coalesce(deposit, 0.00))-sum(coalesce(withdraw, 0.00))
from where tran_date<=dt::date and acc_no='3'), 0.00) amt
from (
select tran_date, min(balance) balance,
coalesce((select sum(coalesce(deposit, 0.00) - coalesce(withdraw, 0.00))
from transaction where tran_date<t.tran_date and acc_no=t.acc_no), 0.00) coAmt
from (
select tran_id, acc_no, tran_date, deposit, withdraw,
sum(deposite - withdraw) over (order by tran_id) balance
from transaction sv group by tran_id, acc_no, tran_date, deposite, withdraw) t
where acc_no='3' group by tran_date, acc_no order by tran_date ) t1
right join
generate_series('2010-12-01', '2010-12-31', interval '1 day') as dt on dt=tran_date
group by dt, tran_date, balance, coAmt order by dt
再次感谢您的帮助。