简体   繁体   English

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

[英]Getting the sum of two columns from two different tables

I have two MySQL tables, with the following structure (I have removed irrelevant columns). 我有两个具有以下结构的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    |                |
+------------------+------------------+------+-----+---------+----------------+

The way the system works is that you order a meal and each meal has a cost, each of these orders are stored in edinners_details . 系统的工作方式是您订购一顿饭,每顿饭都有成本,这些订单中的每一个都存储在edinners_details An example row would be as follows: 示例行如下:

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

Typically people will pay for these meals in bulk - if they have £40 worth of meals over the course of 20 days, they'll pay that off at the end of the month. 通常,人们会批量支付这些餐点的费用-如果他们在20天内获得了价值40英镑的餐点,他们将在月底付清。 Every time they pay, a new row goes into the edinners_payments table, an example row for this would be as follows: 他们每次付款时,都会在edinners_payments表中加入新的一行,示例行如下:

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

So from these two rows we can see that this person is current £5 in debt - they've had a £25 meal and only paid £20. 因此,从这两行中我们可以看到此人目前负债5英镑-他们一顿饭25英镑,只付了20英镑。 Over time there will be many rows for each of the users of the system, I can easily work out how much worth of food they've had by doing a simple query such as 随着时间的流逝,系统的每个用户都会有很多行,我可以通过执行简单的查询(例如

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

Then to get the amount of money they have paid, I simply do this query: 然后,要获取他们已支付的金额,我只需执行以下查询:

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

My ultimate goal is to be able to see who owes the most money, but to loop every user of my users table and run these two queries for them, I believe it would be quite slow, so ideally what I would like to do is combine the two above queries into one, and perhaps an additional column which would be ( meal_total - payment_total ) which would give me the amount owed. 我的最终目标是能够看到谁欠的钱最多,但是要循环我users表中的每个用户并为其运行这两个查询,我相信这会很慢,因此理想情况下,我想做的是结合上述两种查询为一体,也许一个额外的列这将是( meal_total - payment_total ),这会给我所欠款项。 I've tried a few methods to make this work, including joins and sub-queries but they all seem to repeat every relevant line from the edinners_details for each of the edinners_payments line - so if there were 3 details and 4 payments, you would have 12 rows pulled out which means doing a SUM() on the columns gives me a value which far exceeds what it should be. 我尝试了几种方法来完成这项工作,包括联接和子查询,但是它们似乎都对edinners_details每行重复edinners_details中的每个edinners_payments行-因此,如果有3个细节和4个付款,您将拥有拔出12行,这意味着对列执行SUM()会给我一个远远超出应有的值。 A good way to demonstrate this would be to run this query: 演示此问题的一个好方法是运行以下查询:

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`;

which gives me the following result: 这给了我以下结果:

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

Adding the SUM into this, as such: 将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`;

Gives me the following result: 给我以下结果:

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

If this was working, details_cost would be 12.5 and payment_total would be 1229, but that is not the case. 如果details_cost ,则details_cost为12.5, payment_total为1229,但实际情况并非如此。 You can clearly see the repetition in the above result, I apologise that all the costs are 2.5, it makes it a little less obvious, but they are 5 seperate meal orders with 4 payments having been made. 您可以清楚地看到上述结果中的重复,对不起,所有费用均为2.5,这使费用降低了一些,但它们是5份单独的餐点,已支付4笔款项。 Does anyone know how I would go about getting the SUM() of the meal order costs and the SUM() of the payments at the same time? 有谁知道我要如何同时获取餐单成本的SUM()和付款的SUM()?

Thanks 谢谢

I have only PostgreSQL available at hand and this is what I came up with: 我手头只有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;

It's grouping records in each table by pupil_id to properly count the sum and then joins them to get the difference. 它将每个表中的记录按瞳孔ID进行分组,以正确计算总和,然后将它们合并以得到差值。 There is full outer join to handle cases when someone doesn't have any payments (but has dinners) and doesn't have any dinners (but has payments). 当某人没有任何付款(但有晚餐)且没有任何晚餐(但有付款)时,可以使用完全外部联接来处理情况。

From what I've read MySQL doesn't support FULL OUTER JOIN (bump...) so you must emulate it with UNION: 从我读到的内容来看,MySQL不支持FULL OUTER JOIN(凸点...),因此您必须使用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;

Currently, your query is performing a CROSS JOIN , which joins every row from the first table to every row in the second, and so is returning a lot of redundant results. 当前,您的查询正在执行CROSS JOIN ,它将第一张表中的每一行连接到第二张表中的每一行,因此返回了大量冗余结果。 Both tables have a pupil_id , however, so we can use this to join the correct records from each table. 但是,两个表都有一个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;

You could take this further by performing a join to your users table and returning all the data you need in a single query. 您可以通过对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;

The following works for me, although it looks ugly. 以下内容对我有用,尽管看上去很丑。 In a MySQL DB: 在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

You could try something like the following which should return you the amount of debit and id of the pupil with the greatest debt (if a pupil has overpaid his debt will be negative): 您可以尝试以下类似的方法,该方法应向您退还负债最大的学生的借方和身份证(如果学生多付了其债务,则为负):

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;

so if you have the following situation: 因此,如果您有以下情况:

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

when running the above query you should get: 运行上面的查询时,您应该获得:

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

if you like a list of debits for each pupil you can run: 如果您喜欢每个学生的借方清单,则可以运行:

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;

which should get you this result: 这应该给你这个结果:

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

I hope this helps. 我希望这有帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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