[英]MySQL - Subtracting SUMs with tricky joins and a group by
我有两个表将账单链接到账单上的费用,第三个表将付款链接到费用。 我正在尝试确定每张账单是否已全额支付费用。
第一个表“bill_charges”中的记录显示,对于每个 bill_nbr,都有一个关联的 bill_id(一个具有两个 bill_id,因此有多个版本)。
CREATE TABLE bill_charges (
`bill_nbr` varchar(30) NOT NULL,
`bill_id` int(11) NOT NULL,
`version` enum('1','2') NOT NULL DEFAULT '1'
);
INSERT INTO bill_charges (bill_nbr, bill_id, version)
VALUES ('50', 500, '1'),
('50', 501, '2'),
('60', 600, '1');
第二个表“charges”中的记录显示,对于“bill_charges”中的第二个 bill_id,存在多种不同类型和金额的费用。
CREATE TABLE charges (
`charge_id` int(11) NOT NULL AUTO_INCREMENT,
`charge_type_id` int(11) NOT NULL,
`bill_id` int(11) NOT NULL,
`charge_amt` decimal(13,2) NOT NULL DEFAULT '0.00'
);
INSERT INTO charges (charge_id, charge_type_id, bill_id, charge_amt)
VALUES (10, 100, 501, 15.00),
(11, 100, 501, -5.00),
(12, 200, 501, 50.00),
(13, 300, 501, -5.00),
(14, 300, 501, -5.00),
(15, 300, 501, -25.00);
第三个表“付款”中的记录显示,其中三项费用(13、14、15)用于支付其他两项费用(10、12)。
CREATE TABLE payments (
`payment_id` int(11) NOT NULL AUTO_INCREMENT,
`pays_charge_id` int(11) NOT NULL,
`charge_id` int(11) NOT NULL,
`payment_amt` decimal(13,2) NOT NULL DEFAULT '0.00'
);
INSERT INTO payments (payment_id, pays_charge_id, charge_id, payment_amt)
VALUES (20, 13, 10, -5.00),
(21, 14, 10, -5.00),
(20, 15, 12, -25.00);
为了说明这一点,一位客户支付了多件商品,然后退回了一些。 对于这种特殊情况,第一个版本的费用随着退货而消失,因此只有最新版本的账单 (#2) 会产生费用。
费用显示有一项费用从 15 美元减少到 10 美元(charge_ids 10 和 11 的总和)。 第二次收费为 50 美元 (charge_id = 12)。 最后,通过查看支付表,您可以看到 charge_ids 13、14 和 15 根本不是真正的费用,而是对其他两项费用的支付。
我的目标是通过查看针对每个 bill_id 上的费用进行的所有付款,找出一种特定费用类型 (100) 的余额。 我还想知道其他账单上是否没有针对相同收费类型的收费或付款。 我的最终结果应该是:
bill_nbr bill_id version remaining_balance
50 500 1 NULL
50 501 2 0
60 600 1 NULL
我最初的方法是从获取所有 bill_charges 信息并将其正确分组开始。 接下来,我通过 LEFT JOIN 添加费用信息,并对charge_type_id = 100 的金额求和。
SELECT bills.bill_nbr, bills.bill_id,
bills.version, SUM(charges.amount) AS total_charges
FROM bill_charges AS bills
LEFT JOIN charges
ON charges.bill_id = bills.bill_id
AND charges.charge_type_id = 100
GROUP BY bill_id, version;
这给了我一个好的开始,通过显示该类型的费用的总和而没有付款。
bill_nbr bill_id version total_charges
50 500 1 NULL
50 501 2 10
60 600 1 NULL
这是我遇到问题的地方。 如果我再次 LEFT JOIN 到付款表并对付款求和,则剩余余额总额为 -$5.00 而不是 $0。
SELECT bills.bill_nbr, bills.bill_id,
bills.version,
SUM(charges.amount) - SUM(payments.amount) AS remaining_balance
FROM bill_charges AS bills
LEFT JOIN charges
ON charges.bill_id = bills.bill_id
AND charges.charge_type_id = 100
LEFT JOIN payments
ON payments.charge_id = charges.charge_id
GROUP BY bill_id, version;
我可以完成这项工作的唯一方法是使用凌乱的查询。 在现实世界中,所有三个表的大小都非常大——这会导致性能问题。
SELECT x.bill_nbr, x.bill_id, x.version,
x.total - y.total AS remaining_balance
FROM (
SELECT t1.bill_nbr, t1.bill_id, t1.version
SUM(t2.amount) AS total
FROM bill_charges AS t1
LEFT JOIN charges AS t2
ON t2.bill_id = t1.bill_id
AND t2.charge_type_id = 100
GROUP BY t1.bill_id, t1.version
) x
LEFT JOIN (
SELECT t1.bill_nbr, t1.bill_id, t1.version
SUM(t3.amount) AS total
FROM bill_charges AS t1
LEFT JOIN charges AS t2
ON t2.bill_id = t1.bill_id
AND t2.charge_type_id = 100
LEFT JOIN payments AS t3
ON t3.charge_id = t2.charge_id
GROUP BY t1.bill_id, t1.version
) y
ON x.bill_id = y.bill_id
AND x.version = y.version
GROUP BY x.bill_id, x.version;
从您的第二个查询中,进行了两项修改:
您可以尝试此解决方案,请参阅db<>fiddle :
SELECT bills.bill_nbr, bills.bill_id,
bills.version,
SUM(charges.charge_amt) + SUM(payments.payment_amt) AS remaining_balance
FROM bill_charges AS bills
LEFT JOIN charges
ON charges.bill_id = bills.bill_id
AND charges.charge_type_id = 100
LEFT JOIN (
SELECT charge_id, SUM(payment_amt) AS payment_amt
FROM payments
GROUP BY charge_id
) payments
ON payments.charge_id = charges.charge_id
GROUP BY bills.bill_nbr, bills.bill_id,
bills.version
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.