基于历史累计值拆分付款金额至对应品类的SQL实现求助
交易金额按品类拆分解决方案
问题说明
我有如下交易数据,需要新增NUT_AMT和BOLT_AMT两列,用于计算每笔交易对应到BOLTS或NUTS品类的金额。SALE类型的行处理逻辑简单,但PAYMENT类型的行需基于此前对应品类的累计未结清销售额拆分金额。我已用窗口函数实现新列的累计求和,但付款金额的拆分逻辑无法完成,附上输入输出示例表,寻求技术帮助。
输入数据
| ACCOUNTID | ENTRYDATE | TRANSACTION | TYPE | AMOUNT |
|---|---|---|---|---|
| 18 | 2025-07-02 08:00 | PAYMENT | CASH | -2359.53 |
| 18 | 2024-19-12 08:04 | SALE | NUTS | 133 |
| 18 | 2024-19-12 08:04 | SALE | NUTS | 75 |
| 18 | 2024-19-12 07:36 | SALE | BOLTS | 1085.71 |
| 18 | 2024-25-06 07:40 | SALE | BOLTS | 1065.82 |
| 18 | 2024-14-01 15:30 | PAYMENT | CASH | -1065.82 |
| 18 | 2023-26-12 13:07 | SALE | BOLTS | 1065.82 |
| 18 | 2023-10-08 12:36 | PAYMENT | CASH | -1017.91 |
| 18 | 2023-27-06 10:53 | SALE | BOLTS | 1017.91 |
| 18 | 2023-07-02 10:44 | PAYMENT | CASH | -1017.91 |
| 18 | 2022-28-12 09:54 | SALE | BOLTS | 1017.91 |
| 18 | 2022-04-08 00:00 | PAYMENT | CASH | -941.07 |
| 18 | 2022-23-06 06:45 | SALE | BOLTS | 941.07 |
期望输出
| ACCOUNTID | ENTRYDATE | TRANSACTION | TYPE | AMOUNT | NUT_AMT | BOLT_AMT |
|---|---|---|---|---|---|---|
| 18 | 2025-07-02 08:00 | PAYMENT | CASH | -2359.53 | -208 | -2151.53 |
| 18 | 2024-19-12 08:04 | SALE | NUTS | 133 | 133 | 0 |
| 18 | 2024-19-12 08:04 | SALE | NUTS | 75 | 75 | 0 |
| 18 | 2024-19-12 07:36 | SALE | BOLTS | 1085.71 | 0 | 1085.71 |
| 18 | 2024-25-06 07:40 | SALE | BOLTS | 1065.82 | 0 | 1065.82 |
| 18 | 2024-14-01 15:30 | PAYMENT | CASH | -1065.82 | 0 | -1065.82 |
| 18 | 2023-26-12 13:07 | SALE | BOLTS | 1065.82 | 0 | 1065.82 |
| 18 | 2023-10-08 12:36 | PAYMENT | CASH | -1017.91 | 0 | -1017.91 |
| 18 | 2023-27-06 10:53 | SALE | BOLTS | 1017.91 | 0 | 1017.91 |
| 18 | 2023-07-02 10:44 | PAYMENT | CASH | -1017.91 | 0 | -1017.91 |
| 18 | 2022-28-12 09:54 | SALE | BOLTS | 1017.91 | 0 | 1017.91 |
| 18 | 2022-04-08 00:00 | PAYMENT | CASH | -941.07 | 0 | -941.07 |
| 18 | 2022-23-06 06:45 | SALE | BOLTS | 941.07 | 0 | 941.07 |
解决方案(SQL实现)
要实现付款金额按未结清品类销售额拆分,需按时间倒序处理交易,跟踪每个品类的累计应收款,再按规则分配付款金额。以下是基于PostgreSQL的实现代码:
WITH sorted_transactions AS ( SELECT *, ROW_NUMBER() OVER (PARTITION BY ACCOUNTID ORDER BY ENTRYDATE DESC) AS rn FROM transactions ), initial_amt AS ( SELECT *, CASE WHEN TRANSACTION = 'SALE' AND TYPE = 'NUTS' THEN AMOUNT ELSE 0 END AS NUT_AMT, CASE WHEN TRANSACTION = 'SALE' AND TYPE = 'BOLTS' THEN AMOUNT ELSE 0 END AS BOLT_AMT FROM sorted_transactions ), running_balance AS ( SELECT *, SUM(NUT_AMT) OVER (PARTITION BY ACCOUNTID ORDER BY rn) AS running_nut_balance, SUM(BOLT_AMT) OVER (PARTITION BY ACCOUNTID ORDER BY rn) AS running_bolt_balance FROM initial_amt ), payment_split AS ( SELECT *, CASE WHEN TRANSACTION = 'PAYMENT' THEN LEAST(-AMOUNT, running_nut_balance) * -1 ELSE NUT_AMT END AS final_nut_amt, CASE WHEN TRANSACTION = 'PAYMENT' THEN (-AMOUNT - LEAST(-AMOUNT, running_nut_balance)) * -1 ELSE BOLT_AMT END AS final_bolt_amt FROM running_balance ) SELECT ACCOUNTID, ENTRYDATE, TRANSACTION, TYPE, AMOUNT, final_nut_amt AS NUT_AMT, final_bolt_amt AS BOLT_AMT FROM payment_split ORDER BY ENTRYDATE DESC;
逻辑说明
- sorted_transactions:按账户分组,交易时间倒序排序,生成行号确保处理顺序从新到旧。
- initial_amt:初始化SALE类型的品类金额,PAYMENT类型暂设为0。
- running_balance:计算截至当前行的累计应收余额(倒序处理时,余额为未被付款覆盖的销售额)。
- payment_split:付款行优先结清NUTS品类的应收余额,剩余部分分配给BOLTS品类,得到最终拆分金额。
内容的提问来源于stack exchange,提问作者user944796
相关产品推荐
相关产品推荐

