Как обновлять значения базы данных, полученные из курсора в процедуре строка за строкой? - PullRequest
0 голосов
/ 09 ноября 2019

Допустим, у нас есть база данных, касающаяся кредитов клиентам в бизнесе, у нас есть таблица кредитов (в простейшем виде, 5 столбцов):

- ID           int(10) PK Auto_Increment,
- CustomerID   int(10),
- Amount       decimal(10,2),
- TotalLoan    decimal(10,2),
- Date         datetime

, так как клиенты ежедневно берут кредиты, мы вставляем новыезаписи в таблицу, TotalLoan является кредитом клиентов до этого времени. таким образом, в случае редактирования старой записи и исправления Amount денег, TotalLoan этой записи и всех более новых, так как отредактированная дата записи также должны быть исправлены. Подход состоит в том, чтобы сделать сотни запросов на обновление в программе для обновления каждой записи, что я считаю неэффективным и не лучшим способом. Второй подход, о котором я думаю, заключается в использовании программного кода (PHP или Java), который, на мой взгляд, является хорошим способом, если мы не можем выполнить его с помощью процедур MySQL (что наиболее эффективно), но проблема в том, что я не могуобновите значение TotalLoan для этой строки постоянно в каждом цикле, поэтому вот пример кода, который я пробовал до сих пор:

DELIMITER //

create procedure update_loans(IN CID INT)
BEGIN 

    DECLARE amount DECIMAL DEFAULT 0;
    DECLARE loan DECIMAL DEFAULT 0;
    DECLARE current_total_loan DECIMAL DEFAULT 0;

    DECLARE result CURSOR FOR
        SELECT Amount, TotalLoan FROM loans WHERE CustomerID = CID ORDER BY Date ASC;

    open result;
    result_loop: LOOP
        FETCH result INTO amount, loan;
        IF finished = 1 THEN
            LEAVE result_loop;
        END IF;

        SET current_total_loan = current_total_loan + amount; 
        set loan = current_total_loan; -- changing loan variable doesn't change actual stored database value for loan in current row.

    END LOOP result_loop;
    close result;

END

1 Ответ

1 голос
/ 09 ноября 2019

Как я понимаю - вы хотите пересчитать значения в столбце TotalLoan для конкретного клиента. Начиная с вашего подхода курсора, я не вижу другого пути, кроме как выполнить инструкцию UPDATE для каждой найденной строки. Давайте посмотрим, что может сказать нам руководство :

MySQL поддерживает курсоры внутри хранимых программ. Синтаксис такой же, как во встроенном SQL. Курсоры имеют следующие свойства:

  • Asensitive: сервер может или не может сделать копию своей таблицы результатов
  • Только чтение: не обновляется
  • Без возможности прокрутки: может проходить только в одном направлении и не может пропускать строки

Я выделил важную часть жирным шрифтом: " Только для чтения: не обновляется ".

Итак, еще раз: вам нужно выбрать идентификатор строки (в вашем случае это ID) и использовать его в предложении WHERE оператора UPDATE в цикле. Следующая процедура работает для меня:

DELIMITER //

create procedure update_loans(IN CID INT)
BEGIN 

    DECLARE v_id INT;
    DECLARE v_amount DECIMAL(10,2);
    DECLARE current_total_loan DECIMAL(10,2) DEFAULT 0;

    DECLARE finished INT DEFAULT 0;
    DECLARE result CURSOR FOR
        SELECT ID, Amount FROM loans WHERE CustomerID = CID ORDER BY Date ASC;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET finished = 1;

    open result;
    result_loop: LOOP
        FETCH result INTO v_id, v_amount;
        IF finished = 1 THEN
            LEAVE result_loop;
        END IF;

        SET current_total_loan = current_total_loan + v_amount; 

        UPDATE loans SET TotalLoan = current_total_loan WHERE ID = v_id;

    END LOOP result_loop;
    close result;

END //

DELIMITER ;

См. Демонстрацию по db-fiddle

Если вы используете MySQL 8.0+ или MariaDB 10.2+, вы можете добиться того жес одним оператором UPDATE, использующим SUM() в качестве оконной функции (суммарная сумма) в подзапросе:

DELIMITER //

create procedure update_loans(IN CID INT)
BEGIN 

    UPDATE loans l
    JOIN (
        SELECT ID, SUM(Amount) OVER (ORDER BY Date) as new_total
        FROM loans
        WHERE CustomerID = CID
    )x USING(ID)
    SET l.TotalLoan = x.new_total;

END //

DELIMITER ;

См. демонстрацию по db-fiddle

Поскольку этопроцедура с одним оператором, я просто выполняю инструкцию UPDATE из приложения вместо создания процедуры.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...