我有两个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()?
由于
答案 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 |
+-------+-------+
我希望这会有所帮助。