MySQL 处理游标最后一行

在 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)

说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
公众号