- 问题描述:《MySQL必知必会》中的P179存储过程执行完成之后在新表中重复插入了一行。
- 存储过程processorders(计算出orders表中每个订单的总金额并存入一张新表)
#创建一个存储过程用于计算出每个订单的总金额并存放在一张新表中
DELIMITER //
CREATE PROCEDURE processorders()
BEGIN
#声明局部变量
DECLARE done BOOLEAN DEFAULT 0;
DECLARE o INT;
DECLARE t DECIMAL(8,2);
#声明游标
DECLARE ordernumbers CURSOR
FOR
SELECT order_num FROM orders;
#声明异常处理,当sql状态为02000时,将done设置为1
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = 1;
#创建一个新表用于存储结果
CREATE TABLE IF NOT EXISTS ordertotals
(order_num INT,
total DECIMAL(8,2)
);
#打开游标
OPEN ordernumbers;
#循环插入所有行
REPEAT
#获得订单号,行内指针指向最后一行的下一行处时执行该语句会使得SQLSTATE='02000'
#导致上方句柄处理执行使得done = 1
FETCH ordernumbers INTO o;
#计算出带税总金额,结果会返回给参数t,该存储过程在最下面
CALL ordertotal(o,1,t);
#将订单号和总金额插入表中
INSERT INTO ordertotals(order_num,total)
VALUES(o,t);
UNTIL done #结束循环条件为done为非0
END REPEAT;
#关闭游标
CLOSE ordernumbers;
END//
#执行查询语句
CALL processorders()//
- 得到的结果
- 问题分析:当游标内指针指向最后一行的下一行时,再次执行
FETCH ordernumbers INTO o;
该语句时,要取当前行并且指针下移一行,因为此时指针指的位置非合法行,所以取出行错误,导致上方句柄处理触发,使得done = 1,而此时o则没有被插入新数据,还是上一轮循环的旧数据(即最后一行的订单号),所以导致了最后一行重复插入了两次。
-
解决方法:在FETCH语句执行后立即判断done是否被修改为1,若被修改为1,则不执行后面的插入代码了。上方存储过程代码修改为
DELIMITER //
CREATE PROCEDURE processorders()
BEGIN
#声明局部变量
DECLARE done BOOLEAN DEFAULT 0;
DECLARE o INT;
DECLARE t DECIMAL(8,2);
#声明游标
DECLARE ordernumbers CURSOR
FOR
SELECT order_num FROM orders;
#声明异常处理,当sql状态为02000时,将done设置为1
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = 1;
#创建一个新表用于存储结果
CREATE TABLE IF NOT EXISTS ordertotals
(order_num INT,
total DECIMAL(8,2)
);
#打开游标
OPEN ordernumbers;
#循环插入所有行
REPEAT
#获得订单号,行内指针指向最后一行的下一行处时执行该语句会使得SQLSTATE='02000'
#导致上方句柄处理执行使得done = 1
FETCH ordernumbers INTO o;
IF !done THEN #修正的关键位置
#计算出带税总金额,结果会返回给参数t,该存储过程在最下面
CALL ordertotal(o,1,t);
#将订单号和总金额插入表中
INSERT INTO ordertotals(order_num,total)
VALUES(o,t);
END IF;
UNTIL done
END REPEAT;
#关闭游标
CLOSE ordernumbers;
END//
再次查询ordertotals表结果为
参考资料: