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

如何在SQL中实现付款记录与发票/费用账单的按序分配?

实现付款按顺序分配至发票的SQL方案

发票数据表(invoice_data)

customer_idscheduled_payment_datescheduled_total_payment
10042021-04-08 00:00:001300
10042021-04-29 00:00:001300
10042021-05-13 00:00:001300
10042021-06-11 00:00:001300
10042021-06-26 00:00:001300
10042021-07-12 00:00:001300
10042021-07-26 00:00:001300
10032021-04-05 00:00:002012
10032021-04-21 00:00:002012
10032021-05-05 00:00:002012
10032021-05-17 00:00:002012
10032021-06-02 00:00:002012
10032021-06-17 00:00:002012

付款数据表(payment_data)

customer_idpayment_datetotal_payment
10032021-04-06 00:00:002012
10032021-04-16 00:00:002012
10032021-05-03 00:00:002012
10032021-05-18 00:00:002012
10032021-06-01 00:00:002012
10032021-06-17 00:00:002012
10042021-04-06 00:00:001300
10042021-04-22 00:00:00200
10042021-04-27 00:00:002600
10042021-06-11 00:00:001300

需求说明

需要将付款按优先分配给最早的发票的规则进行分配:付清当前最早的未结清发票后,剩余付款金额再分配给下一笔最早的发票,以此类推,最终得到每笔付款对应分配到各发票的金额。

预期结果

customer_idpayment_datescheduled_payment_datetotal_paymentpayment_allocationscheduled_total_payment
10042021-04-06 00:00:002021-04-08 00:00:00130013001300
10042021-04-22 00:00:002021-04-29 00:00:002002001300
10042021-04-27 00:00:002021-04-29 00:00:00260011001300
10042021-04-27 00:00:002021-05-13 00:00:00260013001300
10042021-04-27 00:00:002021-06-11 00:00:0026002001300
10042021-06-11 00:00:002021-06-11 00:00:00130011001300
10042021-06-11 00:00:002021-06-26 00:00:0013002001300
10032021-04-06 00:00:002021-04-05 00:00:00201220122012
10032021-04-16 00:00:002021-04-21 00:00:00201220122012
10032021-05-03 00:00:002021-05-05 00:00:00201220122012
10032021-05-18 00:00:002021-05-17 00:00:00201220122012
10032021-06-01 00:00:002021-06-02 00:00:00201220122012
10032021-06-17 00:00:002021-06-17 00:00:00201220122012

SQL实现方案

核心思路是先为每个客户的发票和付款计算累计金额,再通过区间匹配确定付款的分配对象和金额(兼容PostgreSQL、SQL Server、MySQL 8+等支持窗口函数的数据库):

WITH invoice_with_running_total AS (
    SELECT 
        customer_id,
        scheduled_payment_date,
        scheduled_total_payment,
        SUM(scheduled_total_payment) OVER (
            PARTITION BY customer_id 
            ORDER BY scheduled_payment_date
            ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
        ) AS invoice_running_total,
        SUM(scheduled_total_payment) OVER (
            PARTITION BY customer_id 
            ORDER BY scheduled_payment_date
            ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING
        ) AS prev_invoice_running_total
    FROM invoice_data
),
payment_with_running_total AS (
    SELECT 
        customer_id,
        payment_date,
        total_payment,
        SUM(total_payment) OVER (
            PARTITION BY customer_id 
            ORDER BY payment_date
            ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
        ) AS payment_running_total,
        SUM(total_payment) OVER (
            PARTITION BY customer_id 
            ORDER BY payment_date
            ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING
        ) AS prev_payment_running_total
    FROM payment_data
),
payment_invoice_matches AS (
    SELECT 
        p.customer_id,
        p.payment_date,
        p.total_payment,
        i.scheduled_payment_date,
        i.scheduled_total_payment,
        -- 计算当前付款可分配给当前发票的金额
        GREATEST(
            0,
            LEAST(
                p.payment_running_total, i.invoice_running_total
            ) - GREATEST(
                p.prev_payment_running_total, COALESCE(i.prev_invoice_running_total, 0)
            )
        ) AS payment_allocation
    FROM payment_with_running_total p
    JOIN invoice_with_running_total i 
        ON p.customer_id = i.customer_id
        -- 匹配付款累计区间与发票累计区间有重叠的记录
        AND p.payment_running_total > COALESCE(i.prev_invoice_running_total, 0)
        AND p.prev_payment_running_total < i.invoice_running_total
)
SELECT 
    customer_id,
    payment_date,
    scheduled_payment_date,
    total_payment,
    payment_allocation,
    scheduled_total_payment
FROM payment_invoice_matches
WHERE payment_allocation > 0
ORDER BY customer_id, payment_date, scheduled_payment_date;

代码解释

  1. invoice_with_running_total:为每个客户的发票按日期排序,计算累计金额(invoice_running_total)和上一笔累计金额(prev_invoice_running_total),标记每笔发票对应的金额区间。
  2. payment_with_running_total:同理,为每个客户的付款按日期排序,计算累计金额和上一笔累计金额,标记每笔付款对应的金额区间。
  3. payment_invoice_matches:将付款和发票按客户关联,匹配两者累计金额有重叠的记录,通过区间计算得出该付款分配到对应发票的金额。
  4. 最后过滤掉分配金额为0的记录,按客户、付款日期、发票日期排序得到结果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 16:31:15