如何计算 Partial Payment 和 Outstanding Payment
支付老李
结论
使用 LEFT JOIN 关联发票表和付款表,再通过条件聚合分别统计现金付款与部分付款。未付余额用发票金额减去该发票的全部付款金额计算。
SELECT
i.InvDate,
i.InvNo,
i.InvAmount,
SUM(CASE
WHEN p.ResType = 'C' THEN p.ResAmount
ELSE 0
END) AS Cash,
SUM(CASE
WHEN p.ResType = 'P' THEN p.ResAmount
ELSE 0
END) AS Partial,
i.InvAmount
- COALESCE(SUM(p.ResAmount), 0) AS Balance
FROM Invoice AS i
LEFT JOIN Payment AS p
ON p.InvNo = i.InvNo
GROUP BY
i.InvDate,
i.InvNo,
i.InvAmount
ORDER BY
i.InvDate,
i.InvNo;
查询结果如下:
InvDate InvNo InvAmount Cash Partial Balance
01/01/2016 T0001 3000 3000 0 0
02/01/2016 T0002 8000 0 4000 4000
计算原理
一张发票可能有多条付款记录,所以不能直接选择 ResAmount,而应按发票分组汇总:
ResType = 'C'的金额计入Cash。ResType = 'P'的金额计入Partial。Balance = InvAmount - 所有已付金额。LEFT JOIN会保留尚无付款记录的发票。COALESCE(..., 0)会在没有付款时把NULL转换为0。
条件聚合的核心写法是:
SUM(CASE WHEN p.ResType = 'C' THEN p.ResAmount ELSE 0 END)
这段表达式只累计现金付款,其他付款类型按 0 处理。部分付款也按相同方式计算。
将无金额显示为 NULL
如果希望没有现金付款或部分付款时返回 NULL,再由报表界面显示为 -,可以省略 ELSE 0:
SELECT
i.InvDate,
i.InvNo,
i.InvAmount,
SUM(CASE
WHEN p.ResType = 'C' THEN p.ResAmount
END) AS Cash,
SUM(CASE
WHEN p.ResType = 'P' THEN p.ResAmount
END) AS Partial,
i.InvAmount
- COALESCE(SUM(p.ResAmount), 0) AS Balance
FROM Invoice AS i
LEFT JOIN Payment AS p
ON p.InvNo = i.InvNo
GROUP BY
i.InvDate,
i.InvNo,
i.InvAmount
ORDER BY
i.InvDate,
i.InvNo;
此时空值会显示为:
InvDate InvNo InvAmount Cash Partial Balance
01/01/2016 T0001 3000 3000 NULL 0
02/01/2016 T0002 8000 NULL 4000 4000
通常应让 SQL 返回数值或 NULL,再由展示层将 NULL 格式化为 -。如果直接在 SQL 中混用数字和字符串 '-',相关列可能被转换为字符串,影响后续排序和计算。
注意事项
如果 Payment 表中还有退款、冲销或其他不应计入已付金额的 ResType,余额就不能直接使用 SUM(p.ResAmount),而应明确限定付款类型:
i.InvAmount - COALESCE(
SUM(
CASE
WHEN p.ResType IN ('C', 'P') THEN p.ResAmount
ELSE 0
END
),
0
) AS Balance
还要确保 Invoice.InvNo 能唯一标识一张发票。如果发票编号只在特定客户、公司或账期内唯一,关联和分组条件也要加入相应字段,否则不同发票的付款可能会被汇总到一起。
备注:内容仅供参考。
