MySQL中如何多表多列求和并计算供应商期末余额?
问题描述
现有4张数据表:
- 供应商表(Suppliers)
| ID_A | Supplier_name |
|---|---|
| 1 | Apple |
| 2 | Xiaomi |
| 3 | Nokia |
| 4 | Oppo |
- 期初余额表(Start Balance)
| ID_B | Start Balance |
|---|---|
| 1 | 1000 |
| 2 | 1000 |
| 3 | 1000 |
| 4 | Null |
- 发票表(Invoices)
| ID_C | Invoice_value |
|---|---|
| 1 | 200 |
| 1 | 500 |
| 2 | 800 |
| 3 | 250 |
| 3 | 400 |
| 4 | Null |
- 退货表(Returns)
| ID_D | Return_value |
|---|---|
| 1 | 100 |
| 2 | 50 |
| 2 | 25 |
| 3 | Null |
| 4 | Null |
计算逻辑:Start Balance + 发票总额 - 退货总额 = End Balance
尝试使用UNION结合JOIN编写的MySQL语句如下:
SELECT null , Supplier_name , ID_A , SUM(Invoice_value) , null , null FROM Suppliers inner JOIN Invoices ON ID_A = ID_C group by ID_A UNION ALL SELECT null , Supplier_name , ID_A , null , SUM(Return_value), null FROM Suppliers left JOIN Returns ON ID_A = ID_D group by ID_A UNION ALL SELECT `Start Balance` , Supplier_name, ID_A , null , null ,( `Start Balance` + ifnull(SUM(Invoice_value),0) - ifnull(SUM(Return_value),0) ) FROM Suppliers left JOIN `Start Balance` ON ID_A = ID_B left JOIN Invoices ON ID_A = ID_C left JOIN Returns ON ID_A = ID_D group by ID_A
执行后结果分散在不同行,且期末余额计算错误,期望得到如下格式的结果:
| Start Balance | Supplier_name | ID_A | Invoice_value | Return_value | End_Balance |
|---|---|---|---|---|---|
| 1000 | Apple | 1 | 700 | 100 | 1600 |
| 1000 | Xiaomi | 2 | 800 | 75 | 1725 |
| 1000 | Nokia | 3 | 650 | null | 1650 |
| null | Oppo | 4 | null | null | null |
解决方案
正确的做法是先对发票表和退货表按供应商ID单独聚合,计算出每个供应商的发票总额和退货总额,再将这些聚合结果与供应商表、期初余额表关联,最后计算期末余额。这样可以避免多表直接连接导致的重复计算问题,同时保证所有数据在同一行展示。
SQL代码如下:
SELECT sb.`Start Balance`, s.Supplier_name, s.ID_A, inv.Total_Invoice AS Invoice_value, ret.Total_Return AS Return_value, -- 按需求处理null场景计算期末余额 CASE WHEN sb.`Start Balance` IS NULL THEN NULL ELSE sb.`Start Balance` + IFNULL(inv.Total_Invoice, 0) - IFNULL(ret.Total_Return, 0) END AS End_Balance FROM Suppliers s LEFT JOIN `Start Balance` sb ON s.ID_A = sb.ID_B LEFT JOIN ( -- 聚合每个供应商的有效发票总额 SELECT ID_C, SUM(Invoice_value) AS Total_Invoice FROM Invoices WHERE Invoice_value IS NOT NULL GROUP BY ID_C ) inv ON s.ID_A = inv.ID_C LEFT JOIN ( -- 聚合每个供应商的有效退货总额 SELECT ID_D, SUM(Return_value) AS Total_Return FROM Returns WHERE Return_value IS NOT NULL GROUP BY ID_D ) ret ON s.ID_A = ret.ID_D ORDER BY s.ID_A;
代码说明
- 子查询
inv:按供应商ID聚合发票表,过滤掉null值后计算每个供应商的发票总额。 - 子查询
ret:按供应商ID聚合退货表,过滤掉null值后计算每个供应商的退货总额。 - 主查询:通过左连接关联所有表,确保所有供应商都被包含,不会因为无发票/退货数据被排除。
- 期末余额计算:使用
CASE语句处理期初余额为null的场景,直接返回null;其余情况按公式计算,用IFNULL处理无发票/退货的情况。
内容的提问来源于stack exchange,提问作者abou yahya
相关产品推荐
相关产品推荐

