2012-02-02 96 views
2

我有两个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 | 
+------------+------------------+--------------+ 

,通常人们会在散装这些饭菜支付 - 如果他们有£40,价值超过20天的过程饭菜,他们会支付其关闭,在结束这个月。他们每次付出的时间,一个新行进入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()?

感谢

回答

1

我下面的作品,虽然看起来丑陋。在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 
+0

你不会看到学生没有任何付款(但与晚餐)和学生没有晚餐(但与付款)。 – Furgas 2012-02-02 11:51:11

+0

现在,它应该同时做到这一点。 – ekh 2012-02-02 12:25:23

+0

你好, 非常感谢你,这正是我所期待的。我稍微修改了它,以便将债务计算添加到其中,并仅检索负债的人,这使我留下了[此查询](http://pastebin.com/g4GcLFhj)。再次感谢。 (另外:为什么我的新线路不能在这里工作?我正在做双空间..) – 2012-02-02 12:57:24

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

你好加里, 感谢您的回复,我已经运行您提供的第一个查询直接到我的测试表,但我似乎仍然得到同样的问题 - 表1中的每一行连接到表2中的行。令人讨厌的是,我没有评论中的空间来粘贴查询结果,但它与原始文章中的非常相似。有任何想法吗? 谢谢。 – 2012-02-02 11:29:06

+1

我会建议使用Furgas的答案上面。我已经针对测试数据库运行了他的查询,并返回了您正在查找的结果:http://sqlfiddle.com/#!2/6fa3c/9。祝你好运! – 2012-02-02 12:00:47

+0

感谢您使用SQLFiddle的链接,我之前没有看到它,它可能在未来有用。谢谢你的帮助。 – 2012-02-02 13:12:02

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(凹凸...),所以你必须使用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; 
+0

你好Furgas,谢谢你的回复。出于某种原因,与query @ e.alhajri给出的查询相比,您的查询给了我一个非常不同的结果。我将对我所拥有的数据进行手动记录,并查看哪些数据是正确的。再次感谢。 – 2012-02-02 13:01:45

+0

啊不,我的错误;这只是正面/负面是相反的。你的确给了我正确的数据,谢谢你的帮助。可悲的是,我不能将两个人标记为正确的,也不能给出业力,但放心 - 我会尽我所能。 – 2012-02-02 13:10:53

0

你可以尝试像以下这应该返回您提供最大的债务瞳孔的借记卡和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 | 
+-------+-------+ 

我希望这有助于。

相关问题