Blog Details

Home技能搭配MySQL——游标(cursor)

MySQL——游标(cursor)

一、什么是游标?

游标(Cursor) 是MySQL中用于逐行处理查询结果集的数据库对象。它类似于指针,允许开发者在结果集中逐行移动,并对每一行数据进行特定操作。游标将传统的集合操作转换为面向过程的记录处理方式,特别适用于需要逐行逻辑判断或复杂计算的场景。

为什么需要游标?

逐行处理:如根据每行数据动态生成计算结果或触发业务逻辑。

复杂逻辑:需要基于当前行数据状态执行条件分支操作。

个性化操作:不同记录需要不同的处理策略(如生成定制化报告)。

二、游标的优缺点:权衡使用场景

优点

灵活性强:支持逐行数据访问与操作。

内存高效:分批处理大型结果集,避免一次性加载内存溢出。

过程化控制:可在循环中结合条件判断和变量计算。

缺点

性能开销:比集合操作(如JOIN、子查询)效率低。

资源消耗:占用数据库连接资源,长时间未关闭可能导致阻塞。

复杂度高:代码量增加,调试和维护难度大。

三、游标操作流程:五步掌握核心用法

1. 声明游标

DECLARE cursor_name CURSOR FOR

SELECT column1, column2 FROM table WHERE condition;

作用:定义游标名称和关联的查询,不执行查询。

2. 打开游标

OPEN cursor_name;

作用:执行关联的SELECT语句,生成结果集。

3. 获取数据

FETCH cursor_name INTO var1, var2;

作用:将当前行数据存入变量,游标下移一行。

4. 处理结束条件

DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;

作用:当FETCH无更多数据时触发,设置结束标志。

5. 关闭游标

CLOSE cursor_name;

作用:释放游标占用的资源,必须显式调用。

四、实战案例:游标典型应用场景

案例1:生成学生成绩评估报告

需求:为每个学生的每门课程生成等级和建议。

DELIMITER $$

CREATE PROCEDURE generate_grade_reports()

BEGIN

DECLARE v_sid INT;

DECLARE v_score DECIMAL(5,2);

DECLARE done INT DEFAULT 0;

-- 声明游标:获取所有学生成绩

DECLARE grade_cursor CURSOR FOR

SELECT sid, score FROM t_score;

DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;

OPEN grade_cursor;

grade_loop: LOOP

FETCH grade_cursor INTO v_sid, v_score;

IF done THEN LEAVE grade_loop; END IF;

-- 根据分数生成等级

CASE

WHEN v_score >= 90 THEN

INSERT INTO t_report VALUES (v_sid, 'A', '优秀');

WHEN v_score >= 80 THEN

INSERT INTO t_report VALUES (v_sid, 'B', '良好');

-- 更多条件...

END CASE;

END LOOP;

CLOSE grade_cursor;

END $$

DELIMITER ;

案例2:统计学生总分与平均分

需求:逐学生计算总分、平均分并汇总。

DELIMITER $$

CREATE PROCEDURE calculate_student_stats()

BEGIN

DECLARE v_sid INT;

DECLARE v_total, v_avg DECIMAL;

DECLARE done INT DEFAULT 0;

-- 声明游标:遍历学生

DECLARE student_cursor CURSOR FOR

SELECT sid FROM t_student;

DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;

OPEN student_cursor;

student_loop: LOOP

FETCH student_cursor INTO v_sid;

IF done THEN LEAVE student_loop; END IF;

-- 计算当前学生的统计值

SELECT SUM(score), AVG(score) INTO v_total, v_avg

FROM t_score WHERE sid = v_sid;

INSERT INTO t_stats VALUES (v_sid, v_total, v_avg);

END LOOP;

CLOSE student_cursor;

END $$

DELIMITER ;

案例3:批量调整课程成绩

需求:为某课程所有学生成绩增加固定分值,不超过100分。

DELIMITER $$

CREATE PROCEDURE adjust_scores(IN course_id INT, IN adjust DECIMAL)

BEGIN

DECLARE v_sid INT;

DECLARE v_old_score DECIMAL;

DECLARE done INT DEFAULT 0;

-- 声明游标:获取指定课程成绩

DECLARE score_cursor CURSOR FOR

SELECT sid, score FROM t_score WHERE cid = course_id;

DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;

OPEN score_cursor;

adjust_loop: LOOP

FETCH score_cursor INTO v_sid, v_old_score;

IF done THEN LEAVE adjust_loop; END IF;

-- 计算新成绩并更新

UPDATE t_score SET score = LEAST(v_old_score + adjust, 100)

WHERE sid = v_sid AND cid = course_id;

END LOOP;

CLOSE score_cursor;

END $$

DELIMITER ;

五、常见问题与解决方案

问题1:游标死循环

现象:存储过程无法退出循环。

原因:未正确处理NOT FOUND条件。

解决:确保声明CONTINUE HANDLER并设置终止标志。

问题2:游标性能低下

现象:处理速度慢,数据库负载高。

原因:循环内执行复杂查询或大量计算。

解决:预先计算中间结果,使用临时表存储数据。

问题3:资源泄漏

现象:连接数异常增长,数据库响应变慢。

原因:未显式关闭游标。

解决:在结束处理后务必执行CLOSE。

六、最佳实践与优化建议

限制使用场景:优先使用集合操作,仅在必须逐行处理时使用游标。

优化查询语句:游标关联的SELECT语句需高效,避免全表扫描。

批量提交事务:在循环内定期COMMIT,减少锁竞争。

资源及时释放:处理完成后立即关闭游标。

监控性能:使用EXPLAIN分析查询计划,检查执行时间。

七、总结

适用场景:

逐行数据校验(如格式检查)

动态生成报告(如个性化评价)

级联更新/删除(如历史数据迁移)

慎用场景:

大数据量处理(性能敏感)

高并发业务(资源竞争激烈)

Copyright © 2088 霹雳侠职业教学与活动专题 All Rights Reserved.
友情链接