在 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
mysql 广告位
由于我们可能希望在获取数据后对其进行处理,因此我们不能让异常在未处理的情况下从存储过程中传播出去。因此,我们将在存储过程中添加一个 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)