一、MySQL 游标的特点和使用限制

特点:
只读:MySQL 游标只能用于读取查询结果,不能直接通过游标修改数据
单向:只能按照从前到后的顺序逐行读取,不支持回退、回滚或跳跃读取
敏感:游标基于实际数据集,其他连接对数据的变更可能影响游标读取结果
临时:游标通常只在存储过程或函数执行期间有效
限制:
只能在 MySQL 存储过程或函数中使用
不支持滚动游标功能(只能向前移动)
性能开销相对较大,实际开发中应谨慎使用
二、MySQL 游标的使用步骤
1. 声明游标 → 2. 打开游标 → 3. 逐行读取数据 → 4. 关闭游标
三、声明游标 (DECLARE CURSOR)
语法:
DECLARE cursor_name CURSOR FOR select_statement;
示例:
DELIMITER //
CREATE PROCEDURE process_students()
BEGIN
声明一个学生游标
DECLARE student_cursor CURSOR FOR
SELECT id, name, score FROM tb_student WHERE score > 60;
其他变量声明与业务逻辑...
END //
DELIMITER ;
四、打开游标 (OPEN)
语法:
OPEN cursor_name;
注意:打开游标后,游标并不会立即指向第一条记录,而是停留在第一条记录之前,需通过 FETCH 才能读取第一行数据。
五、读取游标 (FETCH)
语法:
FETCH cursor_name INTO var1, var2, ...;
重要:必须提前声明与查询结果列一一对应的变量,用于接收游标返回的数据。
六、关闭游标 (CLOSE)
语法:
CLOSE cursor_name;
最佳实践:建议在使用完成后显式关闭游标,以便及时释放资源,不要完全依赖 MySQL 自动关闭机制。
七、游标与异常处理
在 MySQL 游标遍历过程中,通常需要定义 `NOT FOUND` 处理程序,用于判断游标是否已经读取完毕。
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
八、完整示例
示例1:基本游标使用(遍历所有学生记录)
DELIMITER //
CREATE PROCEDURE display_all_students()
BEGIN
DECLARE v_id INT;
DECLARE v_name VARCHAR(50);
DECLARE v_score DECIMAL(5,2);
DECLARE done INT DEFAULT 0;
1. 声明游标
DECLARE student_cursor CURSOR FOR
SELECT id, name, score FROM tb_student;
2. 定义异常处理
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
3. 打开游标
OPEN student_cursor;
4. 循环读取数据
read_loop: LOOP
FETCH student_cursor INTO v_id, v_name, v_score;
IF done THEN
LEA VE read_loop;
END IF;
处理每一行学生数据(此处仅作演示)
SELECT CONCAT('ID:', v_id, '姓名:', v_name, '分数:', v_score) AS student_info;
END LOOP;
5. 关闭游标
CLOSE student_cursor;
END //
DELIMITER ;
示例2:使用 WHILE 循环处理游标
DELIMITER //
CREATE PROCEDURE update_low_scores()
BEGIN
DECLARE v_id INT;
DECLARE v_score DECIMAL(5,2);
DECLARE done INT DEFAULT 0;
DECLARE score_cursor CURSOR FOR
SELECT id, score FROM tb_student WHERE score < 60;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN score_cursor;
FETCH score_cursor INTO v_id, v_score;
WHILE done = 0 DO
更新低分学生成绩
UPDATE tb_student
SET score = score + 10
WHERE id = v_id;
FETCH score_cursor INTO v_id, v_score;
END WHILE;
CLOSE score_cursor;
END //
DELIMITER ;
示例3:使用 REPEAT 循环的游标
DELIMITER //
CREATE PROCEDURE process_user_data(out result TEXT)
BEGIN
DECLARE v_name VARCHAR(60);
DECLARE v_pass VARCHAR(64);
DECLARE done INT DEFAULT 0;
DECLARE user_cursor CURSOR FOR
SELECT user_name, user_pass FROM users;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
SET result = '';
OPEN user_cursor;
REPEAT
FETCH user_cursor INTO v_name, v_pass;
IF NOT done THEN
SET result = CONCAT_WS(';', result, CONCAT(v_name, ':', v_pass));
END IF;
UNTIL done END REPEAT;
CLOSE user_cursor;
END //
DELIMITER ;
九、游标使用的最佳实践
1. 尽量减少游标的使用:基于集合的 SQL 操作通常比游标遍历更高效
2. 尽快关闭游标:缩短资源占用时间,降低系统开销
3. 选择合适的循环结构:
`LOOP` + `LEA VE`: 控制更灵活
`WHILE`: 先判断条件,再执行逻辑
`REPEAT`: 先执行一次,再进行条件判断
4. 优先批量处理:如果业务允许,尽量使用批量更新而不是逐行处理
5. 做好错误处理:始终加入合适的异常处理机制
十、游标的性能考虑
游标的缺点:
会增加数据库服务器的处理开销
会占用一定的锁资源和内存资源
执行效率通常低于基于集合的 SQL 操作
替代方案考虑:
使用 `UPDATE ... WHERE ...` 替代逐行更新操作
借助临时表处理更复杂的业务逻辑
在应用层完成分页查询和数据遍历
十一、嵌套游标
MySQL 支持嵌套游标,但在实际项目中需要谨慎使用:
可以声明一个 CONTINUE HANDLER,当游标没有更多数据时,将 done_inner 和 done_outer 都设置为 1。
OPEN outer_cursor;
FETCH outer_cursor INTO ...;
WHILE done_outer = 0 DO
OPEN inner_cursor;
FETCH inner_cursor INTO ...;
WHILE done_inner = 0 DO
执行具体处理逻辑
FETCH inner_cursor INTO ...;
END WHILE;
CLOSE inner_cursor;
SET done_inner = 0;
FETCH outer_cursor INTO ...;
END WHILE;
CLOSE outer_cursor;
