更新mysql中的余额

Posted

技术标签:

【中文标题】更新mysql中的余额【英文标题】:Update balances in mysql 【发布时间】:2020-08-15 03:08:41 【问题描述】:

我有一个包含 5 列的 Operation 表:

身份证 bankAccount_id 值日期 借记(始终为正) 学分 余额

我想更新一个银行账户所有操作的余额。

在阅读了一些 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中的余额的主要内容,如果未能解决你的问题,请参考以下文章

MySQL使用Select更新查询

如何使用 django 更新会计应用程序中的余额?

仅在 Stripe 中的平台余额更新时收到通知 - WebHook

具有实时余额更新的大容量交易的最佳实践

如何在 Google Cloud Datastore 上更新交易中的帐户余额

无法更新双倍(余额)