我的数据库我有一个装满物品的桌子。 每件商品都可以有一系列付款 一旦用户付款,用户就可以通过兑换它来使用该钱。钱可以部分兑换。
对于每次兑换,我想计算剩余的钱。这等于执行SUM(paid_amount) - SUM(previous_redemptions) - this_redemption
。
问题是,如果由于SQL连接的性质(一行是连接的组合)而导致多次先前的兑换,则每次付款将被计数多次。
我真的不确定我想要的只能使用MySQL来计算(并且仍然相当快)。
如果我能以某种方式使每个SQL组只包含一行,每列都有多个值,那么问题就会解决,我认为SQL不支持这种行。
mysql> DESCRIBE items;
+-------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| id | int(11) | NO | PRI | NULL | |
+-------+---------+------+-----+---------+-------+
mysql> DESCRIBE payments;
+-------------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------------+---------+------+-----+---------+-------+
| id | int(11) | NO | PRI | NULL | |
| item_id | int(11) | NO | | NULL | |
| paid_amount | int(11) | NO | | NULL | |
+-------------+---------+------+-----+---------+-------+
mysql> DESCRIBE redemptions;
+-------------+----------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------------+----------+------+-----+---------+-------+
| id | int(11) | NO | PRI | NULL | |
| item_id | int(11) | NO | | NULL | |
| amount | int(11) | YES | | NULL | |
| create_time | datetime | YES | | NULL | |
+-------------+----------+------+-----+---------+-------+
mysql> SELECT * FROM items;
+----+
| id |
+----+
| 1 |
+----+
mysql> SELECT * FROM payments;
+----+---------+-------------+
| id | item_id | paid_amount |
+----+---------+-------------+
| 1 | 1 | 50 |
| 2 | 1 | 50 |
+----+---------+-------------+
mysql> SELECT * FROM redemptions;
+----+---------+--------+---------------------+
| id | item_id | amount | create_time |
+----+---------+--------+---------------------+
| 1 | 1 | 10 | 2013-01-01 00:00:00 |
| 2 | 1 | 10 | 2013-01-01 00:01:00 |
| 3 | 1 | 10 | 2013-01-01 00:02:00 |
+----+---------+--------+---------------------+
mysql> SELECT
-> redemptions.id AS redemption_id,
-> SUM(payments.paid_amount) - COALESCE(SUM(previous_redemptions.amount), 0) - redemptions.amount AS remaining_balance
-> FROM redemptions
-> JOIN payments ON payments.item_id = redemptions.item_id
-> LEFT JOIN redemptions AS previous_redemptions
-> ON
-> previous_redemptions.item_id = redemptions.item_id AND
-> previous_redemptions.create_time < redemptions.create_time
-> GROUP BY redemptions.id;
+---------------+-------------------+
| redemption_id | remaining_balance |
+---------------+-------------------+
| 1 | 90 |
| 2 | 70 |
| 3 | 150 |
+---------------+-------------------+
正如你所看到的,这并不是我想要的那样。我希望兑换3的remaining_balance
为70。
这意味着使子查询首先计算每次兑换的所有使用金额(之前兑换的总和)是不可能的。
CREATE TABLE items (
id int(11) NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE payments (
id int(11) NOT NULL,
item_id int(11) NOT NULL,
paid_amount int(11) NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE redemptions (
id int(11) NOT NULL,
item_id int(11) NOT NULL,
amount int(11),
create_time datetime,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO items VALUES (1);
INSERT INTO payments VALUES
(1, 1, 50),
(2, 1, 50);
INSERT INTO redemptions VALUES
(1, 1, 10, '2013-01-01 00:00:00'),
(2, 1, 10, '2013-01-01 00:01:00'),
(3, 1, 10, '2013-01-01 00:02:00');
SELECT
redemptions.id AS redemption_id,
SUM(payments.paid_amount) - COALESCE(SUM(previous_redemptions.amount), 0) - redemptions.amount AS remaining_balance
FROM redemptions
JOIN payments ON payments.item_id = redemptions.item_id
LEFT JOIN redemptions AS previous_redemptions
ON
previous_redemptions.item_id = redemptions.item_id AND
previous_redemptions.create_time < redemptions.create_time
GROUP BY redemptions.id;
答案 0 :(得分:2)
这里你去!
SELECT
redemptions.id AS redemption_id,
(payments.paid_amount - SUM(previous_redemptions.amount)) AS remaining_balance
FROM redemptions
JOIN payments ON payments.id = redemptions.payment_id
LEFT JOIN redemptions AS previous_redemptions
ON
previous_redemptions.payment_id = redemptions.payment_id AND
previous_redemptions.id <= redemptions.id
GROUP BY redemptions.id;
答案 1 :(得分:1)
这使用用户定义的变量来保持总计:
SELECT
r.id AS redemption_id,
@totAmt:=SUM(p.paid_amount)-COALESCE(@sumAmt,r.amount) totAmt,
@sumAmt:=COALESCE(@sumAmt,r.amount)+r.amount
FROM redemptions r
JOIN payments p ON p.id = r.payment_id
JOIN (SELECT @sumAmt:=NULL, @totAmt:=0) s
GROUP BY r.id
ORDER BY r.create_time
编辑:给定评论,您可以使用相关子查询:
SELECT
r.id AS redemption_id,
SUM(p.paid_amount)-
(SELECT SUM(r2.amount)
FROM redemptions r2
WHERE r.item_id = r2.item_id AND
r2.create_time <= r.create_time )
FROM redemptions r
JOIN payments p ON p.item_id = r.item_id
GROUP BY r.id
ORDER BY r.create_time
答案 2 :(得分:1)
鉴于多次付款和多次兑换问题,我能想到的最好的就是:
SELECT
redemptions.id AS redemption_id,
payment_totals.paid_amount - SUM(cumulative_redemptions.amount) AS remaining_balance
FROM redemptions
INNER JOIN
(SELECT item_id, SUM(paid_amount) paid_amount FROM payments GROUP BY item_id) payment_totals
ON
redemptions.item_id = payment_totals.item_id
INNER JOIN redemptions AS cumulative_redemptions
ON
cumulative_redemptions.item_id = redemptions.item_id AND
cumulative_redemptions.create_time <= redemptions.create_time
GROUP BY redemptions.id, payment_totals.paid_amount;
这使用子查询来计算每个项目的总付款数,但在您的问题中没有明确禁止,但我不保证其性能。由于您在评论中提到批量更新为sgeddes,另一种选择是创建一个包含索引的实际payment_totals
表,并在运行批处理的其余部分之前重新填充它。