MySQL 游标详解:声明、使用与存储过程实战
了解 MySQL 游标(Cursor)的生命周期,包括 DECLARE 声明、OPEN 打开、FETCH 读取和 CLOSE 关闭四个核心步骤。通过创建一个存储过程实现按工资排序逐条累加计算薪资总和的实战案例,解决面向集合的 SQL 在处理单行逻辑时的局限。
什么是游标
在 SQL 查询中,我们通常直接返回整个结果集,而难以像指针一样灵活地向前或向后定位特定记录进行处理。游标提供了一种机制,允许程序指向存储在表中的某一行,并支持对当前行的数据进行操作。这使得基于集合操作的 SQL 语言具备了面向过程的处理能力。
关键点:
- 游标是一种临时的数据库对象,通常在
存储过程和函数中使用。 - 游标充当指针角色,可以逐行访问
SELECT语句的结果集。 - 它解决了需要在应用层进行循环处理才能完成的逐行逻辑问题。
如何使用游标
在使用游标前需明确其严格的声明顺序。在 存储过程 体内部,变量的声明必须先于游标,而游标的声明又必须先于错误处理程序(Handler)的声明。
第1步:声明游标 (DECLARE)
目的: 将 SELECT 查询语句绑定到一个名称上,此时尚未开始读取数据。
入口/位置: 存储过程 内部的 BEGIN 之后。
动作: 定义游标名及其对应的查询语句。
代码:
DECLARE 游标名 CURSOR FOR SELECT 语句;
示例: 声明一个名为 emp_cur 的游标,获取所有员工的 ID 和薪资:
DECLARE emp_cur CURSOR FOR
SELECT employee_id, salary FROM employees;
第2步:打开游标 (OPEN)
目的: 执行 SELECT 语句并将结果集加载到内存工作区,为逐行读取做准备。
动作: 调用 OPEN 命令激活刚才声明的游标。
代码:
OPEN 游标名;
注意: 打开后,游标指针指向结果集的第一行之前。
第3步:使用游标 (FETCH)
目的: 从结果集中取出当前行的数据并存入变量,同时移动指针到下一行。
入口: 循环结构(如 WHILE, REPEAT, LOOP)内部。
动作: 将字段值映射到预先定义的变量中。
参数要求: FETCH 后面的变量数量必须与 SELECT 返回的列数一致。
代码:
FETCH 游标名 INTO 变量1, 变量2 ...;
示例:
FETCH emp_cur INTO emp_id, emp_sal;
风险提示: 如果读取到的行数少于预期,或者已经到达结果集末尾但未检测到结束状态,继续 FETCH 可能会导致错误或死循环,建议配合 CONTINUE HANDLER 来检测 NOT FOUND 状态。
第4步:关闭游标 (CLOSE)
目的: 释放游标占用的系统资源(如内存和锁),防止影响并发效率。
动作: 在不需要使用该游标时及时调用 CLOSE。
代码:
CLOSE 游标名;
说明: 关闭后的游标不可再次使用,如需重新读取必须再次执行 OPEN。
存储过程实战举例
假设业务需求是:从工资最高的员工开始累加薪资,当累计总额超过指定阈值时,统计涉及了多少人。 这种逻辑非常适合用游标结合 循环 来处理。
完整代码实现
DELIMITER $
CREATE PROCEDURE get_count_by_limit_total_salary(
IN limit_total_salary DOUBLE,
OUT total_count INT
)
BEGIN
-- 1. 定义变量
DECLARE count_emp INT DEFAULT 0; # 记录符合条件的人数
DECLARE sum_sal DOUBLE DEFAULT 0; # 累加的总薪资
DECLARE one_sal DOUBLE DEFAULT 0; # 当前员工的单份薪资
-- 2. 声明游标:按薪资从高到低排序
DECLARE emp_cur CURSOR FOR
SELECT salary FROM employees ORDER BY salary DESC;
-- 3. 定义处理器:当没有更多数据可读取时,设置标志位
DECLARE CONTINUE HANDLER FOR NOT FOUND SET @done = TRUE;
OPEN emp_cur; # 打开游标
-- 4. 循环读取数据
read_loop: LOOP
FETCH emp_cur INTO one_sal; # 读取一行薪资
IF @done THEN LEAVE read_loop; END IF;
SET sum_sal = sum_sal + one_sal; # 累加薪资
SET count_emp = count_emp + 1; # 人数+1
-- 判断是否达到限制值
IF sum_sal >= limit_total_salary THEN
LEAVE read_loop;
END IF;
END LOOP;
CLOSE emp_cur; # 关闭游标
-- 5. 返回结果
SET total_count = count_emp;
END $
DELIMITER ;
-- 测试调用
CALL get_count_by_limit_total_salary(30000, @count);
SELECT @count AS total_employees;
小结与性能建议
游标通过 逐行读取 提供了灵活的逻辑处理能力,但其代价是资源消耗较大。由于游标操作主要在内存中进行且可能锁定相关行,在高并发场景下容易导致性能瓶颈甚至内存溢出。
最佳实践:
- 及时关闭: 无论操作成功与否,务必在
TRY...CATCH结构或确保路径正常退出时调用CLOSE。 - 替代方案: 如果只是简单的行处理,优先考虑纯 SQL 的
JOIN或窗口函数是否能满足需求,减少程序性遍历。 - 资源管理: 大型数据量下的逐行处理会严重影响效率,请根据实际业务体量谨慎使用。
以上就是关于 MySQL 游标详细介绍及使用方法的详细内容,更多关于 MySQL 高级技巧的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









