从两个不同的表中获取两列的总和

时间:2012-02-02 10:29:54

标签: php mysql math sum

我有两个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()?

由于

4 个答案:

答案 0 :(得分:2)

我手边只有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;

它通过pupil_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;

答案 1 :(得分:1)

目前,您的查询正在执行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;

答案 2 :(得分:1)

以下适用于我,虽然它看起来很难看。在MySQL DB中:

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

答案 3 :(得分:0)

你可以尝试下面这样的东西,它应该给你带来最大债务的学生的借记和账户金额(如果学生多付了他的债务将是负数):

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 |
+-------+-------+

我希望这会有所帮助。