我MySQL 中的游标(Cursor)

2025-05-08 00:31:56

一、游标的作用

​​逐行处理数据​​:当需要对查询结果集中的每一行进行特定操作(如计算、条件判断、调用其他过程)时使用。​​替代集合操作​​:在无法通过单一 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 可能影响游标状态。