一、游标的作用
- 逐行处理数据:当需要对查询结果集中的每一行进行特定操作(如计算、条件判断、调用其他过程)时使用。
- 替代集合操作:在无法通过单一 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()
BEGIN
DECLARE 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: LOOP
FETCH cur INTO emp_salary;
IF done = 1 THEN
LEAVE 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()
BEGIN
DECLARE 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: LOOP
FETCH cur INTO order_id, order_status;
IF done THEN
LEAVE loop_start;
END IF;
IF order_status = 'PENDING' THEN
UPDATE 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
可能影响游标状态。