将一个帐户的现金推/拉到其他帐户,并按帐户跟踪余额

时间:2017-02-28 22:18:03

标签: sql postgresql

我正在试图弄清楚如何将现金从一个帐户推入/拉入其他帐户并按帐户跟踪余额。我能想到解释这个问题的最佳模式是"小额现金"。有一个主要的小额现金"有现金的账户,然后其他账户提取现金并将现金返还(以FIFO方式)。处理完所有交易后,我想知道已经将多少小额现金转移到其他每个账户。

功能

  1. 帐户余额绝不应为负数;一旦小额现金用尽,就不能再将其存入账户
  2. 交易将以FIFO方式处理
  3. 负面账户交易从小额现金账户中扣除资金,正面账户交易金额退回
  4. 示例数据设置

    CREATE TABLE IF NOT EXISTS transactions (
       id serial PRIMARY KEY, account_id int, date DATE, amount int);
    
    INSERT INTO transactions (account_id, date, amount) VALUES
     /* Petty cash account transaction; 
        all other accounts will pull from this cash */
     (1, '2017-01-01', 5),
    
     /* Other account transactions that will
        pull from petty cash */
     (2, '2017-01-02', -2),
     (3, '2017-01-03', -8),
     (4, '2017-01-04', -6),
     (3, '2017-01-05', 10),
     (2, '2017-01-06', 1);
    

    预期输出

    | account_id | cash_balance | notes                                              | 
    |------------|--------------|----------------------------------------------------| 
    | 1          | 0            | Petty cash account depleted                        | 
    | 2          | 1            | Total spending was -1                              | 
    | 3          | 0            | Total spending was +2, we gave all petty cash back | 
    | 4          | 4            | Total spending was -6, we had 4 left to pull       | 
    

1 个答案:

答案 0 :(得分:0)

我终于想出办法来做到这一点。使用两步流程,我首先计算每个帐户交易的“调整金额”,这减少了同一帐户FIFO上任何可用退款的支出。然后,在步骤2中,我使用这些调整后的金额FIFO从小额现金“拉”。

CREATE TABLE IF NOT EXISTS transactions (
   id serial PRIMARY KEY, account_id int, date DATE, amount int);

INSERT INTO transactions (account_id, date, amount) VALUES
 /* Petty cash account transaction; 
    all other accounts will pull from this cash */
 (1, '2017-01-01', 5),

 /* Other account transactions that will
    pull from petty cash */
 (2, '2017-01-02', -2),
 (3, '2017-01-03', -8),
 (4, '2017-01-04', -6),
 (3, '2017-01-05', 10),
 (2, '2017-01-06', 1);


WITH e_adjusted_amounts AS (
    SELECT id, account_id, amount,
        /* adjusted_amount: the amount adjusted for returns, FIFO.
       This gives us a logical amount for outflows, adjusted for corresponding inflows
       that can be used later when we start pulling from petty cash. */
        amount - LEAST(0, GREATEST(amount,
            -(  -- total of all preceeding outflows
                COALESCE((SUM(amount) FILTER (WHERE amount < 0) OVER (PARTITION BY account_id ORDER BY id)) - amount,0)
                -- total of all inflows
              + COALESCE(SUM(amount) FILTER (WHERE amount > 0) OVER (PARTITION BY account_id),0)

             )
            )
        ) as adjusted_amount
    FROM transactions
),
e_pull_amounts AS (
    SELECT a.id, a.account_id, a.amount, a.adjusted_amount, LEAST(0, GREATEST(a.adjusted_amount,
             -(  petty_cash.amount
                 -- total of all preceeding outflows
               + COALESCE((SUM(a.adjusted_amount) OVER (ORDER BY id)) - a.adjusted_amount,0)
             )
            )
        ) as pull_amount
    FROM e_adjusted_amounts a,
        (SELECT amount FROM transactions WHERE account_id = 1) petty_cash
    -- Outflows only
    WHERE adjusted_amount < 0
    AND account_id != 1
)
SELECT account_id, SUM(pull_amount) as cash_balance
FROM e_pull_amounts
GROUP BY account_id
ORDER BY account_id;