【问题标题】:Getting the sum of two columns from two different tables从两个不同的表中获取两列的总和
【发布时间】:2012-02-02 10:29:53
【问题描述】:

我有两个 MySQL 表,结构如下(我已经删除了不相关的列)。

mysql> DESCRIBE `edinners_details`;
+------------------+------------------+------+-----+---------+----------------+
| Field            | Type             | Null | Key | Default | Extra          |
+------------------+------------------+------+-----+---------+----------------+
| details_id       | int(11) unsigned | NO   | PRI | NULL    | auto_increment |
| details_pupil_id | int(11) unsigned | NO   |     | NULL    |                |
| details_cost     | double unsigned  | NO   |     | NULL    |                |
+------------------+------------------+------+-----+---------+----------------+

mysql> DESCRIBE `edinners_payments`;
+------------------+------------------+------+-----+---------+----------------+
| Field            | Type             | Null | Key | Default | Extra          |
+------------------+------------------+------+-----+---------+----------------+
| payment_id       | int(11) unsigned | NO   | PRI | NULL    | auto_increment |
| payment_pupil_id | int(11) unsigned | NO   |     | NULL    |                |
| payment_amount   | float unsigned   | NO   |     | NULL    |                |
+------------------+------------------+------+-----+---------+----------------+

系统的工作方式是您点餐,每餐都有费用,这些订单中的每一个都存储在edinners_details。示例行如下:

mysql> SELECT * FROM `edinners_details` LIMIT 1;
+------------+------------------+--------------+
| details_id | details_pupil_id | details_cost |
+------------+------------------+--------------+
|          1 |            18343 |           25 |
+------------+------------------+--------------+

通常,人们会批量支付这些餐费 - 如果他们在 20 天内有价值 40 英镑的餐费,他们将在月底付清。每次付款时,edinners_payments 表中都会有一个新行,示例行如下:

mysql> SELECT * FROM `edinners_payments` LIMIT 1;
+------------+------------------+----------------+
| payment_id | payment_pupil_id | payment_amount |
+------------+------------------+----------------+
|          1 |            18343 |             20 |
+------------+------------------+----------------+

因此,从这两行中,我们可以看到此人目前欠债 5 英镑 - 他们吃了 25 英镑的饭菜,只付了 20 英镑。随着时间的推移,系统的每个用户都会有很多行,我可以通过一个简单的查询来轻松计算出他们有多少食物价值,例如

SELECT SUM(`details_cost`) AS `meal_total` 
FROM `edinners_details` 
WHERE `details_pupil_id` = '18343';

然后要获得他们支付的金额,我只需进行以下查询:

SELECT SUM(`payment_amount`) AS `payment_total` 
FROM `edinners_payments` 
WHERE `payment_pupil_id` = '18343';

我的最终目标是能够查看谁欠的钱最多,但是循环我的users 表的每个用户并为他们运行这两个查询,我相信这会很慢,所以理想情况下我会喜欢做的是将上述两个查询合并为一个,也许还有一个附加列,即 (meal_total - payment_total),它会给我所欠的金额。我已经尝试了一些方法来完成这项工作,包括连接和子查询,但它们似乎都重复了 edinners_details 中每个 edinners_payments 行中的每个相关行 - 所以如果有 3 个详细信息和 4 个付款,您将拉出 12 行,这意味着对列执行 SUM() 会给我一个远远超过应有的值。证明这一点的一个好方法是运行这个查询:

SELECT * FROM (
    SELECT `details_cost` AS `cost` 
    FROM `edinners_details` 
    WHERE `details_pupil_id` = '18343'
    GROUP BY `details_id`
) AS `details`, (
    SELECT `payment_amount` AS `amount` 
    FROM `edinners_payments` 
    WHERE `payment_pupil_id` = '18343'
    GROUP BY `payment_id`
) AS `payment`;

这给了我以下结果:

+------+--------+
| cost | amount |
+------+--------+
|  2.5 |     20 |
|  2.5 |      6 |
|  2.5 |      3 |
|  2.5 |   1200 |
|  2.5 |     20 |
|  2.5 |      6 |
|  2.5 |      3 |
|  2.5 |   1200 |
|  2.5 |     20 |
|  2.5 |      6 |
|  2.5 |      3 |
|  2.5 |   1200 |
|  2.5 |     20 |
|  2.5 |      6 |
|  2.5 |      3 |
|  2.5 |   1200 |
|  2.5 |     20 |
|  2.5 |      6 |
|  2.5 |      3 |
|  2.5 |   1200 |
+------+--------+

将 SUM 添加到其中,如下所示:

SELECT SUM(`details`.`cost`) AS `details_cost`, SUM(`payment`.`amount`) AS `payment_total` FROM (
    SELECT `details_cost` AS `cost` 
    FROM `edinners_details` 
    WHERE `details_pupil_id` = '18343'
    GROUP BY `details_id`
) AS `details`, (
    SELECT `payment_amount` AS `amount` 
    FROM `edinners_payments` 
    WHERE `payment_pupil_id` = '18343'
    GROUP BY `payment_id`
) AS `payment`;

给我以下结果:

+--------------+---------------+
| details_cost | payment_total |
+--------------+---------------+
|           50 |          6145 |
+--------------+---------------+

如果这有效,details_cost 将是 12.5,payment_total 将是 1229,但事实并非如此。您可以清楚地看到上述结果中的重复,我很抱歉所有费用都是 2.5,这让它不太明显,但它们是 5 份单独的餐点订单,已经支付了 4 次付款。有谁知道我将如何同时获得订餐费用的 SUM() 和付款的 SUM()?

谢谢

【问题讨论】:

    标签: php mysql math sum


    【解决方案1】:

    我手头只有 PostgreSQL,这就是我想出的:

    SELECT coalesce(costs.pupil_id, amounts.pupil_id) as pupil_id,
           coalesce(amount_sum, 0) as amount_sum,
           coalesce(cost_sum, 0) as cost_sum,
           coalesce(amount_sum, 0) - coalesce(cost_sum, 0) as debit
    FROM (
           SELECT details_pupil_id AS pupil_id,
                  sum(details_cost) AS cost_sum
           FROM edinners_details
           GROUP BY details_pupil_id
         ) costs
         FULL OUTER JOIN 
         (
           SELECT payment_pupil_id AS pupil_id,
                  sum(payment_amount) AS amount_sum
           FROM edinners_payments
           GROUP BY payment_pupil_id
         ) amounts ON costs.pupil_id = amounts.pupil_id;
    

    它通过学生id对每个表中的记录进行分组以正确计算总和,然后将它们连接起来以获得差异。当某人没有任何付款(但有晚餐)和没有任何晚餐(但有付款)时,有完整的外部联接来处理情况。

    根据我的阅读,MySQL 不支持 FULL OUTER JOIN (bump...),因此您必须使用 UNION 来模拟它:

    SELECT coalesce(costs.pupil_id, amounts.pupil_id) AS pupil_id,
           coalesce(amount_sum, 0) as amount_sum,
           coalesce(cost_sum, 0) as cost_sum,
           coalesce(amount_sum, 0) - coalesce(cost_sum, 0) AS debit
    FROM (
           SELECT details_pupil_id AS pupil_id,
                  sum(details_cost) AS cost_sum
           FROM edinners_details
           GROUP BY details_pupil_id
         ) costs
         LEFT OUTER JOIN 
         (
           SELECT payment_pupil_id AS pupil_id,
                  sum(payment_amount) AS amount_sum
           FROM edinners_payments
           GROUP BY payment_pupil_id
         ) amounts ON costs.pupil_id = amounts.pupil_id
    UNION
    SELECT coalesce(costs.pupil_id, amounts.pupil_id) AS pupil_id,
           coalesce(amount_sum, 0) as amount_sum,
           coalesce(cost_sum, 0) as cost_sum,
           coalesce(amount_sum, 0) - coalesce(cost_sum, 0) AS debit
    FROM (
           SELECT details_pupil_id AS pupil_id,
                  sum(details_cost) AS cost_sum
           FROM edinners_details
           GROUP BY details_pupil_id
         ) costs
         RIGHT OUTER JOIN 
         (
           SELECT payment_pupil_id AS pupil_id,
                  sum(payment_amount) AS amount_sum
           FROM edinners_payments
           GROUP BY payment_pupil_id
         ) amounts ON costs.pupil_id = amounts.pupil_id;
    

    【讨论】:

    • 您好 Furgas,感谢您的回复。出于某种原因,与@e.alhajri 给出的查询相比,您的查询给了我一个非常不同的结果。我将对我拥有的数据进行手动统计,看看哪个是正确的。再次感谢。
    • 啊不,我错了;只是正面/负面是相反的。你的确实给了我正确的数据,谢谢你的帮助。遗憾的是,我不能将两个人标记为正确,也不能给出业力,但请放心——如果可以的话,我会的。
    【解决方案2】:

    以下内容对我有用,尽管它看起来很丑。在 MySQL 数据库中:

    SELECT
        t1.p_id, t1.cost, t2.amount
    FROM
        (SELECT
            details_pupil_id AS p_id, SUM(details_cost) AS cost
         FROM
            edinners_details
         GROUP BY
            details_pupil_id) t1,
        (SELECT
            payment_pupil_id AS p_id, SUM(payment_amount) AS amount
         FROM
            edinners_payments
         GROUP BY
            payments_pupil_id) t2
    WHERE
        t1.p_id = t2.p_id
    
    /* Getting pupils with dinners but no payment */
    UNION
        SELECT
            details_pupil_id, SUM(details_cost) cost, 0
        FROM
            edinners_details
        WHERE
            details_pupil_id NOT IN (SELECT DISTINCT payment_pupil_id FROM edinners_payments)
        GROUP BY
            details_pupil_id
    
    /* Getting pupils with payment but no dinners */
    UNION
        SELECT
            payment_pupil_id, 0, SUM(payment_amount)
        FROM
            edinners_payments
        WHERE
            payment_pupil_id NOT IN (SELECT DISTINCT details_pupil_id FROM edinners_details)
        GROUP BY
            payment_pupil_id
    

    【讨论】:

    • 您不会看到没有任何付款(但有晚餐)的学生和没有晚餐(但有付款)的学生。
    • 您好,非常感谢您,这正是我想要的。我稍微修改了它,将债务计算添加到其中,只检索负债的人,这给我留下了this query。再次感谢。 (另外:为什么我的新行在这里不起作用?我正在做双空格..)
    【解决方案3】:

    目前,您的查询正在执行CROSS JOIN,它将第一个表中的每一行连接到第二个表中的每一行,因此返回了大量冗余结果。但是,两个表都有一个pupil_id,因此我们可以使用它来连接每个表中的正确记录。

    SELECT
      d.detail_pupil_id AS pupil_id,
      SUM(d.details_cost) AS cost,
      SUM(p.payment_amount) AS amount
    FROM `edinners_details` d
    INNER JOIN `edinners_payments` p ON d.detail_pupil_id = p.payment_pupil_id
    GROUP BY pupil_id;
    

    您可以通过对您的users 表执行连接并在单个查询中返回您需要的所有数据来更进一步。

    SELECT
      users.id,
      users.name,
      payment.cost,
      payment.amount
    FROM `users`
    INNER JOIN (
      SELECT
        d.detail_pupil_id AS pupil_id,
        SUM(d.details_cost) AS cost,
        SUM(p.payment_amount) AS amount
      FROM `edinners_details` d
      INNER JOIN `edinners_payments` p ON d.detail_pupil_id = p.payment_pupil_id
      GROUP BY pupil_id
    ) payment ON payment.pupil_id = users.id
    ORDER BY users.id ASC;
    

    【讨论】:

    • 您好 Gary,感谢您的回复,我已将您提供的第一个查询直接运行到我的测试表上,但我似乎仍然遇到同样的问题 - 表 1 中的每一行都连接到表 2 中的每一行。令人讨厌的是,我在此评论中没有空间粘贴查询结果,但它与原始帖子中的非常相似。有任何想法吗?谢谢。
    • 我建议使用上面 Furgas 的答案。我已经对一个测试数据库运行了他的查询,它返回了您正在寻找的结果:sqlfiddle.com/#!2/6fa3c/9。祝你好运!
    • 感谢您提供指向 SQLFiddle 的链接,我以前没见过它,它将来可能会派上用场。感谢您的帮助。
    【解决方案4】:

    您可以尝试以下类似的方法,它应该返回您欠债最多的学生的借方金额和 ID(如果学生多付了他的债务,则为负数):

    select t.id, max(t.debit) from (select details_pupil_id as id, (sum(details_cost) - (select sum(payment_amount) from edinners_payments where payment_pupil_id = details_pupil_id)) as debit from edinners_details group by details_pupil_id) as t;
    

    所以如果你有以下情况:

    mysql> select * from edinners_details;
    +------------+------------------+--------------+
    | details_id | details_pupil_id | details_cost |
    +------------+------------------+--------------+
    |          1 |            18343 |           25 |
    |          2 |            18344 |           17 |
    |          3 |            18343 |           11 |
    |          4 |            18344 |            2 |
    |          5 |            18344 |            7 |
    |          6 |            18343 |           12 |
    |          7 |            18343 |           12 |
    |          8 |            18343 |           35 |
    |          9 |            18344 |           30 |
    +------------+------------------+--------------+
    
    mysql> select * from edinners_payments;
    +------------+------------------+----------------+
    | payment_id | payment_pupil_id | payment_amount |
    +------------+------------------+----------------+
    |          1 |            18343 |             20 |
    |          2 |            18344 |             25 |
    |          3 |            18343 |             12 |
    |          4 |            18344 |             25 |
    |          5 |            18343 |             22 |
    |          6 |            18344 |             11 |
    |          7 |            18343 |              8 |
    |          8 |            18344 |              2 |
    +------------+------------------+----------------+
    

    运行上述查询时,您应该得到:

    +-------+--------------+
    | id    | max(t.debit) |
    +-------+--------------+
    | 18343 |           33 |
    +-------+--------------+
    

    如果您喜欢每个学生的借记清单,您可以运行:

    select details_pupil_id as id, (sum(details_cost) - (select sum(payment_amount) from edinners_payments where payment_pupil_id = details_pupil_id)) as debit from edinners_details group by details_pupil_id;
    

    这应该会给你这个结果:

    +-------+-------+
    | id    | debit |
    +-------+-------+
    | 18343 |    33 |
    | 18344 |    -7 |
    +-------+-------+
    

    我希望这会有所帮助。

    【讨论】:

      猜你喜欢
      • 2010-11-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-04
      • 2021-06-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多