Обновление балансов в mysql - PullRequest
2 голосов
/ 30 апреля 2020

У меня есть таблица Operation, которая содержит 5 столбцов:

  • id
  • bankAccount_id
  • valueDate
  • дебет (всегда положительный)
  • кредит
  • остаток

Я хочу обновить баланс всех операций банковского счета.

Я закончил с этим SQL запрос после прочтения некоторых вопросов и ответов SO:

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 операций находятся в том же порядке, что и идентификаторы. Но когда сначала выполняются операции с большими идентификаторами, это не работает.

Как видно, операция 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;

1 Ответ

1 голос
/ 30 апреля 2020

Переменные и порядок вместе сложны. Работает ли лучше, если вы сначала заказываете в подзапросе, а затем вычисляете переменную?

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;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...