mysql - 如何逐行更新从过程中的游标检索的数据库值?

标签 mysql stored-procedures

假设我们有一个处理企业客户贷款的数据库,我们有一个贷款表(最简单的 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

最佳答案

据我了解 - 您想要重新计算特定客户的 TotalLoan 列中的值。从您的游标方法开始,除了为每个找到的行执行 UPDATE 语句之外,我没有看到任何其他方法。让我们看看 manual 是什么?可以告诉我们:

MySQL supports cursors inside stored programs. The syntax is as in embedded SQL. Cursors have these properties:

  • Asensitive: The server may or may not make a copy of its result table
  • Read only: Not updatable
  • Nonscrollable: Can be traversed only in one direction and cannot skip rows

我用粗体标记了重要部分:“只读:不可更新”。

同样:您需要选择一个行标识符(在您的情况下为 ID)并在循环中 UPDATE 语句的 WHERE 子句中使用它。以下过程对我有用:

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+,则可以在子查询中使用 SUM() 作为窗口函数(计算和),通过单个 UPDATE 语句实现相同的效果:

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 语句,而不是创建过程。

关于mysql - 如何逐行更新从过程中的游标检索的数据库值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58777392/

相关文章:

php - 按 sku 获取产品 min_price

mysql - 在 MySQL 中模拟 DELETE CASCADE?

c# - 调用存储过程的 Entity Framework 需要未提供的参数

javascript - Ajax 调用一直给我 "internal server error 500"

php - 尝试获取存储过程结果时出错,但普通查询工作正常

php - Mysql for 循环重复相同的结果 - 我可以在传递之前添加唯一的 ID 来查询吗?

PHP:在 mySQL 中插入引号时出错

sql - mysql强制一条记录到顶部

mysql - 其他数据库中的过程表

mysql - 不同表列之间的差异