繁体   English   中英

MySQL - 用棘手的连接和一组减去 SUM

[英]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;

从您的第二个查询中,进行了两项修改:

  • 第 3 行:收费金额总和 + 支付金额总和(之前是减法)
  • 第 8 行:左连接子查询(以前是支付表)

您可以尝试此解决方案,请参阅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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM