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