客户同商品退货单据与采购单精准匹配核销的技术实现需求
采购退货单据匹配核销处理需求
核心需求
针对客户采购数据完成退货单据的匹配核销:删除客户购买**同商品(相同ItemCode、ColorCode)**后,与采购日期最接近的退货发票(IsReturn=1)。
具体规则
- 单商品采购后全额退货:同时删除采购单据与退货单据两行数据;
- 同一采购单据含多件商品,其中一件退货:扣减该采购单据对应商品的
Qty1与Revenue,仅删除退货单据。
现有问题
尝试过sum(Qty1)>0的处理方式,但在大数据量场景下性能无法满足。
输入数据
| CustomerCode | InvoiceNumber | IsReturn | InvoiceDate | ItemCode | ColorCode | Qty1 | Revenue |
|---|---|---|---|---|---|---|---|
| 15656858 | 193-R-7-373740 | 1 | 11.06.2022 | 4A1400000002 | SYH | -1 | -700 |
| 15656858 | 193-R-7-372193 | 0 | 27.05.2022 | 4A1400000002 | SYH | 2 | 1.400 |
| 15656858 | 193-R-7-373742 | 1 | 19.06.2023 | 4A1400000002 | SYH | -1 | -700 |
| 15656858 | 1-R-5-16844678 | 0 | 17.06.2023 | 4A1400000002 | SYH | 1 | 700 |
| 15656858 | 193-R-7-373781 | 1 | 12.06.2022 | 4A2022200036 | YVZ | -1 | -240 |
| 15656858 | 193-R-7-373502 | 0 | 10.06.2022 | 4A2022200036 | YVZ | 1 | 240 |
| 21069880 | 406-R-7-121023 | 0 | 4.05.2023 | 4A0121100002 | YSL | 1 | 200 |
| 21069880 | 406-R-7-120741 | 0 | 26.04.2023 | 4A1400000001 | SYH | 1 | 380 |
| 21069880 | 1-R-7-16894399 | 1 | 26.04.2023 | 4A1400000001 | SYH | -1 | -380 |
| 21069880 | 406-R-7-120740 | 0 | 26.04.2023 | 4A1400000001 | SYH | 1 | 880 |
| 21069880 | 1-R-7-16853468 | 0 | 3.09.2022 | 4A2000000013 | amv | 3 | 2.000 |
| 21069880 | 1-R-7-16894396 | 1 | 7.09.2022 | 4A2000000013 | amv | -1 | -667 |
| 21069880 | 1-R-7-16853468 | 0 | 3.09.2022 | 4A3022100145 | GSY | 1 | 1.587 |
期望输出数据
| CustomerCode | InvoiceNumber | IsReturn | InvoiceDate | ItemCode | ColorCode | Qty1 | Revenue |
|---|---|---|---|---|---|---|---|
| 15656858 | 193-R-7-372193 | 0 | 27.05.2022 | 4A1400000002 | SYH | 1 | 700 |
| 21069880 | 406-R-7-121023 | 0 | 4.05.2023 | 4A0121100002 | YSL | 1 | 200 |
| 21069880 | 406-R-7-120740 | 0 | 26.04.2023 | 4A1400000001 | SYH | 1 | 880 |
| 21069880 | 1-R-7-16853468 | 0 | 3.09.2022 | 4A3022100145 | GSY | 1 | 1.587 |
| 21069880 | 1-R-7-16853468 | 0 | 3.09.2022 | 4A2000000013 | amv | 2 | 1.333 |
解决方案(适配大数据量场景)
使用窗口函数精准匹配退货与采购单据,避免全量聚合的性能损耗,步骤如下:
- 按客户、商品维度分组,为采购/退货单按日期排序,标记序列;
- 匹配每个退货单到最近的未核销采购单;
- 计算核销后的采购单数量与金额,过滤掉全额核销的单据。
示例SQL代码(MySQL):
WITH ranked_invoices AS ( -- 为采购/退货单按日期排序 SELECT *, ROW_NUMBER() OVER ( PARTITION BY CustomerCode, ItemCode, ColorCode, IsReturn ORDER BY STR_TO_DATE(InvoiceDate, '%d.%m.%Y') ) AS rn FROM invoices ), purchase_return_matches AS ( -- 匹配退货单到最近的前置采购单 SELECT p.*, r.InvoiceNumber AS return_invoice, r.Qty1 AS return_qty, r.Revenue AS return_rev, ROW_NUMBER() OVER ( PARTITION BY r.InvoiceNumber ORDER BY ABS(DATEDIFF(STR_TO_DATE(p.InvoiceDate, '%d.%m.%Y'), STR_TO_DATE(r.InvoiceDate, '%d.%m.%Y'))) ) AS match_rank FROM ranked_invoices p JOIN ranked_invoices r ON p.CustomerCode = r.CustomerCode AND p.ItemCode = r.ItemCode AND p.ColorCode = r.ColorCode AND p.IsReturn = 0 AND r.IsReturn = 1 AND STR_TO_DATE(p.InvoiceDate, '%d.%m.%Y') <= STR_TO_DATE(r.InvoiceDate, '%d.%m.%Y') ), valid_matches AS ( -- 保留每个退货单的最优匹配 SELECT * FROM purchase_return_matches WHERE match_rank = 1 ), adjusted_purchases AS ( -- 计算核销后的采购单数据 SELECT p.CustomerCode, p.InvoiceNumber, p.IsReturn, p.InvoiceDate, p.ItemCode, p.ColorCode, p.Qty1 + COALESCE(SUM(v.return_qty), 0) AS adjusted_qty, p.Revenue + COALESCE(SUM(v.return_rev), 0) AS adjusted_rev FROM ranked_invoices p LEFT JOIN valid_matches v ON p.InvoiceNumber = v.InvoiceNumber WHERE p.IsReturn = 0 GROUP BY p.CustomerCode, p.InvoiceNumber, p.IsReturn, p.InvoiceDate, p.ItemCode, p.ColorCode, p.Qty1, p.Revenue ) -- 过滤有效采购单 SELECT CustomerCode, InvoiceNumber, IsReturn, InvoiceDate, ItemCode, ColorCode, adjusted_qty AS Qty1, adjusted_rev AS Revenue FROM adjusted_purchases WHERE adjusted_qty > 0 ORDER BY CustomerCode, InvoiceDate;
方案优势
- 用窗口函数+精准JOIN替代全量聚合,降低大数据量下的计算负载;
- 按日期排序匹配,严格遵循“最近采购单”的核销规则;
- 分步处理逻辑清晰,便于后续优化和维护。
内容的提问来源于stack exchange,提问作者Buğra Varol
相关产品推荐
相关产品推荐

