在 MySQL 存储程序中最常见的操作之一就是通过游标获取一条或多条数据。然而,MySQL(以及 ANSI 标准)认为,试图获取超过游标最后一行的数据是错误的。因此,在对游标的结果进行循环时,几乎总是需要捕捉这种特殊错误(ERROR 1329)。
请看下面示例,该示例使用简单循环语句循环用户信息游标。乍一看,你可能会担心我们无意中创建了一个死循环(myLoop:LOOP ... END LOOP myLoop),因为在循环中我们没有编写任何离开循环的代码(LEAVE)。例如:
CREATE DEFINER=`root`@`localhost` PROCEDURE `demo`() BEGIN -- 声明变量 DECLARE user_id INT; DECLARE user_name VARCHAR(50); -- 声明游标 DECLARE user_cursor CURSOR FOR SELECT id,name FROM `user`; -- 打开游标 OPEN user_cursor; -- 迭代数据 myLoop:LOOP -- 获取一行数据 FETCH user_cursor INTO user_id, user_name; -- 输出数据 SELECT user_id, user_name; END LOOP myLoop; -- 关闭游标 CLOSE user_cursor; -- 输出结束信息 SELECT 'FINISHED.' AS message; END
在 MySQL 客户端调用这个存储程序,发现只要我们试图获取结果集中最后一行以外的内容,这个看似无限的循环就会失败:
mysql> call demo(); +---------+-----------+ | user_id | user_name | +---------+-----------+ | 1 | 王五 | +---------+-----------+ 1 row in set (0.00 sec) +---------+-----------+ | user_id | user_name | +---------+-----------+ | 2 | Helen | +---------+-----------+ 1 row in set (0.00 sec) ERROR 1329 (02000): No data - zero rows fetched, selected, or processed
由于我们可能希望在获取数据后对其进行处理,因此我们不能让异常在未处理的情况下从存储过程中传播出去。因此,我们将在存储过程中添加一个 CONTINUE HANDLER 声明,设置一个标志来指示最后一行已被获取。例如:
CREATE DEFINER=`root`@`localhost` PROCEDURE `demo`() BEGIN -- 声明变量 -- 最后一行的标识,1-表示最后一行,0-表示非最后一行 DECLARE l_last_row INT DEFAULT 0; DECLARE user_id INT; DECLARE user_name VARCHAR(50); -- 声明游标 DECLARE user_cursor CURSOR FOR SELECT id,name FROM `user`; -- 当迭代游标数据到最后一行时,出发该错误处理程序,设置 l_last_row 为 1 DECLARE CONTINUE HANDLER FOR NOT FOUND SET l_last_row=1; -- 打开游标 OPEN user_cursor; -- 迭代数据 myLoop:LOOP -- 获取一行数据 FETCH user_cursor INTO user_id, user_name; -- 如果已经是最后一行了,则离开循环 IF (l_last_row=1) THEN LEAVE myLoop; END IF; -- 输出数据 SELECT user_id, user_name; END LOOP myLoop; -- 关闭游标 CLOSE user_cursor; -- 输出结束信息 SELECT 'FINISHED.' AS message; END
调用修改后的存储程序,输出如下:
mysql> call demo(); +---------+-----------+ | user_id | user_name | +---------+-----------+ | 1 | 王五 | +---------+-----------+ 1 row in set (0.00 sec) +---------+-----------+ | user_id | user_name | +---------+-----------+ | 2 | Helen | +---------+-----------+ 1 row in set (0.00 sec) +-----------+ | message | +-----------+ | FINISHED. | +-----------+ 1 row in set (0.00 sec) Query OK, 0 rows affected (0.00 sec)