MySQL 中的游标(Cursor)
一、游标的作用
- 逐行处理数据:当需要对查询结果集中的每一行进行特定操作(如计算、条件判断、调用其他过程)时使用。
- 替代集合操作:在无法通过单一 SQL 语句完成复杂逻辑时,游标提供逐行处理的能力。
- 典型场景:数据迁移、报表生成、逐行校验等。
二、游标的使用步骤
游标使用遵循 5个固定步骤,必须按顺序编写:
-
声明游标
定义游标名称和对应的查询语句:DECLARE cursor_name CURSOR FOR SELECT_statement;
-
声明错误处理器
处理游标遍历结束时的NOT FOUND
状态,避免死循环:DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
-
打开游标
执行查询语句,生成结果集:OPEN cursor_name;
-
循环获取数据
逐行读取数据并进行处理:FETCH cursor_name INTO variables;
-
关闭游标
释放资源:CLOSE cursor_name;
三、完整示例
示例1:计算员工平均工资
DELIMITER //CREATE PROCEDURE calculate_avg_salary()
BEGINDECLARE emp_salary DECIMAL(10,2);DECLARE total DECIMAL(10,2) DEFAULT 0;DECLARE count INT DEFAULT 0;DECLARE done INT DEFAULT 0;-- 1. 声明游标DECLARE cur CURSOR FOR SELECT salary FROM employees;-- 2. 错误处理器DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;-- 3. 打开游标OPEN cur;-- 4. 循环读取read_loop: LOOPFETCH cur INTO emp_salary;IF done = 1 THENLEAVE read_loop;END IF;SET total = total + emp_salary;SET count = count + 1;END LOOP;-- 5. 关闭游标CLOSE cur;-- 输出结果SELECT total / count AS avg_salary;
END //DELIMITER ;-- 调用存储过程
CALL calculate_avg_salary();
示例2:处理订单状态
CREATE PROCEDURE update_order_status()
BEGINDECLARE order_id INT;DECLARE order_status VARCHAR(20);DECLARE done INT DEFAULT 0;DECLARE cur CURSOR FOR SELECT id, status FROM orders WHERE created_at < NOW() - INTERVAL 30 DAY;DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;OPEN cur;loop_start: LOOPFETCH cur INTO order_id, order_status;IF done THENLEAVE loop_start;END IF;IF order_status = 'PENDING' THENUPDATE orders SET status = 'EXPIRED' WHERE id = order_id;END IF;END LOOP;CLOSE cur;
END;
四、注意事项
-
性能问题
游标逐行操作会 增加数据库负载,数据量大时效率低下,优先考虑集合操作(如UPDATE
、JOIN
)。 -
作用域限制
游标必须在存储过程或函数的 BEGIN-END块 中声明,且所有DECLARE
需放在其他语句之前。 -
只读性
MySQL 游标默认是 只读 的,无法通过游标直接修改数据(需配合UPDATE
语句)。 -
错误处理
必须通过CONTINUE HANDLER
处理NOT FOUND
状态,否则可能陷入死循环。 -
资源释放
游标使用后必须用CLOSE
释放,否则可能导致内存泄漏。 -
事务控制
在事务中使用游标时,注意COMMIT
或ROLLBACK
可能影响游标状态。