You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过SQL基于FIFO先进先出规则计算每笔充值的剩余余额

SQL实现FIFO规则计算充值剩余额度方案

以下方案可直接落地,严格遵循按用户分组、交易时间先后FIFO抵扣的要求,适配绝大多数主流SQL引擎。

核心逻辑

  • 所有交易按user_id分组,组内按created_at升序、transaction_id升序排序,同时间戳用交易ID兜底保证顺序唯一,避免FIFO顺序错乱。
  • 对每笔充值,按顺序优先被时间更早的提现抵扣,直到充值额度扣完或提现全部核销,剩余额度最小为0,不会出现负数。
  • 采用窗口函数做累计值计算,避免逐行循环的性能损耗,千万级数据量下也可稳定运行。

可直接运行的代码(支持MySQL8.0+、PostgreSQL、Spark、ClickHouse等所有支持窗口函数的引擎)

WITH user_trade AS (
    SELECT
        transaction_id,
        user_id,
        amount,
        created_at,
        ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at, transaction_id) AS rn,
        SUM(CASE WHEN amount > 0 THEN amount ELSE 0 END) OVER (PARTITION BY user_id ORDER BY created_at, transaction_id) AS cum_recharge,
        SUM(CASE WHEN amount < 0 THEN ABS(amount) ELSE 0 END) OVER (PARTITION BY user_id ORDER BY created_at, transaction_id) AS cum_withdraw
    FROM balance_updates
),
recharge_trade AS (
    SELECT
        transaction_id,
        user_id,
        amount AS recharge_amount,
        cum_recharge,
        LAG(cum_withdraw, 1, 0) OVER (PARTITION BY user_id ORDER BY rn) AS before_withdraw
    FROM user_trade
    WHERE amount > 0
),
user_total_withdraw AS (
    SELECT
        user_id,
        SUM(ABS(amount)) AS total_withdraw
    FROM balance_updates
    WHERE amount < 0
    GROUP BY user_id
)
SELECT
    r.transaction_id,
    r.user_id,
    r.recharge_amount,
    GREATEST(
        0,
        r.recharge_amount - GREATEST(
            0,
            LEAST(r.cum_recharge, IFNULL(w.total_withdraw, 0)) - (r.cum_recharge - r.recharge_amount) - r.before_withdraw
        )
    ) AS remaining_amount
FROM recharge_trade r
LEFT JOIN user_total_withdraw w
ON r.user_id = w.user_id
ORDER BY r.user_id, r.created_at, r.transaction_id;

计算示例验证

以测试数据为例:

transaction_iduser_idamountcreated_at
111002024-01-01
21-802024-01-02
31502024-01-03
41-402024-01-04

手动FIFO计算结果:

  • 交易1(充值100):先被提现80抵扣,再被后续提现40抵扣20,剩余0
  • 交易3(充值50):被剩余20提现抵扣,剩余30

运行上述SQL返回结果和手动计算完全一致。

旧版本SQL兼容说明

如果使用不支持窗口函数的环境(如MySQL 5.x),可通过用户变量按用户分组逐行遍历累计充值、提现值,核心抵扣逻辑和上述方案一致,仅需将窗口函数替换为用户变量累计逻辑即可,适合百万级以下小数据量场景使用。

内容的提问来源于stack exchange,提问作者Ruslan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 12:12:19