我们在编写 SQL 语句的时候通常是面向集合进行思考,这种思考方式更让我们关注结果集的特征,而不是具体的实现过程。
什么是游标?
它提供了一种灵活的操作方式,可以让我们从数据结果集中每次提取一条数据记录进行操作。
如何使用游标?
使用游标,一般需要经历五个步骤。不同 DBMS 中,使用游标的语法可能略有不同。
- 定义游标
# 定义
DECLARE cursor_name CURSOR FOR select_statement
# 例子
DECLARE cur_hero CURSOR FOR
SELECT hp_max FROM heros;
- 打开游标
OPEN cursor_name
打开游标的时候 SELECT 语句的查询结果集就会送到游标工作区。
- 从游标中取得数据
FETCH cursor_name INTO var_name ...
如果游标读取的数据行有多个列名,则在 INTO 关键字后面赋值给多个变量名即可。
- 关闭游标
CLOSE cursor_name
关闭游标之后,我们就不能再检索查询结果中的数据行,如果需要检索只能再次打开游标。
- 释放游标
DEALLOCATE cursor_namec
一定要养成释放游标的习惯,否则游标会一直存在于内存中,直到进程结束后才会自动释放。
完整例子:
CREATE PROCEDURE `calc_hp_max`()
BEGIN
-- 创建接收游标的变量
DECLARE hp INT;
-- 创建总数变量
DECLARE hp_sum INT DEFAULT 0;
-- 创建结束标志变量
DECLARE done INT DEFAULT false;
-- 定义游标
DECLARE cur_hero CURSOR FOR SELECT hp_max FROM heros;
-- 指定游标循环结束时的返回值
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = true;
OPEN cur_hero;
read_loop:LOOP
FETCH cur_hero INTO hp;
-- 判断游标的循环是否结束
IF done THEN
LEAVE read_loop;
END IF;
SET hp_sum = hp_sum + hp;
END LOOP;
CLOSE cur_hero;
SELECT hp_sum;
END
当游标溢出时(也就是当游标指向到最后一行数据后继续执行会报的错误),我们可以定义一个 continue 的事件,指定这个事件发生时修改变量 done 的值,以此来判断游标是否已经溢出,即:
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = true;
使用游标来解决一些常见的问题
总结
虽然在处理某些复杂的数据情况下,使用游标可以更灵活,但同时也会带来一些性能问题,比如在使用游标的过程中,会对数据行进行加锁,这样在业务并发量大的时候,不仅会影响业务之间的效率,还会消耗系统资源,造成内存不足,这是因为游标是在内存中进行的处理。如果有游标的替代方案,我们可以采用替代方案。