更新mysql中的余额
Posted
技术标签:
【中文标题】更新mysql中的余额【英文标题】:Update balances in mysql 【发布时间】:2020-08-15 03:08:41 【问题描述】:我有一个包含 5 列的 Operation
表:
我想更新一个银行账户所有操作的余额。
在阅读了一些 SO 问题和答案后,我最终得到了这个 SQL 请求:
UPDATE Operation INNER JOIN (
SELECT
id,
credit,
debit,
@Balance := @Balance + IFNULL(credit,0) - IFNULL(debit,0) AS Balance
FROM Operation, (SELECT @Balance := 0) AS variableInit
WHERE bankAccount_id = 1
ORDER BY valueDate ASC, id ASC
) subRequest ON Operation.id = subRequest.id
SET Operation.balance = subRequest.Balance;
当操作的 valueDates 与 IDs 的顺序相同时,它会很好地工作。但是当先有大ID的操作时,就不行了。
如您所见,操作 22 是最后更新的,尽管它是按起息日排序的第一个!
+----+---------------------+--------+---------+---------+
| id | valueDate | debit | credit | balance |
+----+---------------------+--------+---------+---------+
| 22 | 2018-01-01 00:00:00 | NULL | 103.00 | 618.00 |
| 1 | 2020-01-15 14:00:00 | NULL | 1000.00 | 1000.00 |
| 2 | 2020-01-15 15:00:00 | NULL | 200.00 | 1200.00 |
| 3 | 2020-01-15 16:00:00 | 50.00 | NULL | 1150.00 |
| 4 | 2020-01-18 17:00:00 | 200.00 | NULL | 950.00 |
| 5 | 2020-01-19 18:00:00 | NULL | 20.00 | 970.00 |
| 6 | 2020-01-21 20:00:00 | 500.00 | NULL | 470.00 |
| 7 | 2020-01-21 20:00:00 | 10.00 | NULL | 460.00 |
| 8 | 2020-01-21 20:00:00 | NULL | 30.00 | 490.00 |
| 9 | 2020-02-02 01:00:00 | 5.00 | NULL | 485.00 |
| 10 | 2020-02-10 09:00:00 | 10.00 | NULL | 475.00 |
| 11 | 2020-02-11 10:00:00 | NULL | 40.00 | 515.00 |
+----+---------------------+--------+---------+---------+
我不明白的是,当我单独运行内部查询时,它会以正确的顺序和正确的平衡返回操作!
如何使更新查询正常工作?
这是您可以用于测试的数据结构:
CREATE TABLE `Operation` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`valueDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`debit` decimal(10,2) DEFAULT NULL,
`credit` decimal(10,2) DEFAULT NULL,
`balance` decimal(10,2) NOT NULL,
`bankAccount_id` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `Operation` VALUES (1,'2020-01-15 13:00:00',NULL,1000.00,0.00,1),(2,'2020-01-15 14:00:00',NULL,200.00,0.00,1),(3,'2020-01-15 15:00:00',50.00,NULL,0.00,1),(4,'2020-01-18 16:00:00',200.00,NULL,0.00,1),(5,'2020-01-19 17:00:00',NULL,20.00,0.00,1),(6,'2020-01-21 19:00:00',500.00,NULL,0.00,1),(7,'2020-01-21 19:00:00',10.00,NULL,0.00,1),(8,'2020-01-21 19:00:00',NULL,30.00,0.00,1),(9,'2020-02-02 00:00:00',5.00,NULL,0.00,1),(10,'2020-02-10 08:00:00',10.00,NULL,0.00,1),(11,'2020-02-11 09:00:00',NULL,40.00,0.00,1),(12,'2020-01-21 19:00:00',NULL,500.00,0.00,2),(13,'2020-01-22 20:00:00',NULL,15.00,0.00,2),(14,'2020-02-02 00:00:00',NULL,5.00,0.00,2),(15,'2020-02-05 03:00:00',30.00,NULL,0.00,2),(16,'2020-02-10 08:00:00',NULL,60.00,0.00,2),(17,'2019-12-30 22:00:00',NULL,50.00,0.00,3),(18,'2020-01-21 19:00:00',10.00,NULL,0.00,3),(19,'2020-01-21 19:00:00',30.00,NULL,0.00,3),(20,'2020-01-21 19:00:00',100.00,NULL,0.00,3),(21,'2020-01-27 22:00:00',NULL,55.00,0.00,3),(22,'2017-12-31 23:00:00',NULL,103.00,0.00,1);
您可以通过以下方式订购操作:
select * from Operation where bankAccount_id = 1 ORDER BY valueDate ASC, id ASC;
【问题讨论】:
你能告诉我们预期的输出吗? 我认为您向我们展示了输出。你能展示一些样本输入吗?而且,您使用什么版本的 mysql?请edit您的问题。 【参考方案1】:变量和排序在一起很棘手。如果先在子查询中排序,然后计算变量,效果会更好吗?
UPDATE operation o
INNER JOIN (
SELECT
o1.id,
@balance := @balance + IFNULL(o1.credit, 0) - IFNULL(o1.debit, 0) AS balance
FROM (
SELECT id, credit, debit
FROM Operation
WHERE bankAccount_id = 1
ORDER BY valueDate ASC, id ASC
) o1
CROSS JOIN (SELECT @Balance := 0) AS v
) s ON o.id = s.id
SET o.balance = s.balance;
请注意,如果您运行的是 MySQL 8.0,则使用窗口函数完成此操作要简单得多:
UPDATE operation o
INNER JOIN (
SELECT
id,
SUM(IFNULL(o1.credit, 0) - IFNULL(o1.debit, 0))
OVER(ORDER BY valueDate, id) balance
FROM Operation
WHERE bankAccount_id = 1
) s ON o.id = s.id
SET o.balance = s.balance;
【讨论】:
感谢它正在使用版本 5.7.29-0ubuntu0.18.04.1。感谢 MySQL 8.0 的提示!以上是关于更新mysql中的余额的主要内容,如果未能解决你的问题,请参考以下文章
仅在 Stripe 中的平台余额更新时收到通知 - WebHook