我们在编写 SQL 语句的时候通常是面向集合进行思考,这种思考方式更让我们关注结果集的特征,而不是具体的实现过程。

什么是游标?

它提供了一种灵活的操作方式,可以让我们从数据结果集中每次提取一条数据记录进行操作

如何使用游标?

使用游标,一般需要经历五个步骤。不同 DBMS 中,使用游标的语法可能略有不同。

  1. 定义游标
  1. # 定义
  2. DECLARE cursor_name CURSOR FOR select_statement
  3. # 例子
  4. DECLARE cur_hero CURSOR FOR
  5. SELECT hp_max FROM heros;
  1. 打开游标
  1. OPEN cursor_name

打开游标的时候 SELECT 语句的查询结果集就会送到游标工作区。

  1. 从游标中取得数据
  1. FETCH cursor_name INTO var_name ...

如果游标读取的数据行有多个列名,则在 INTO 关键字后面赋值给多个变量名即可。

  1. 关闭游标
  1. CLOSE cursor_name

关闭游标之后,我们就不能再检索查询结果中的数据行,如果需要检索只能再次打开游标。

  1. 释放游标
  1. DEALLOCATE cursor_namec

一定要养成释放游标的习惯,否则游标会一直存在于内存中,直到进程结束后才会自动释放。

完整例子:

  1. CREATE PROCEDURE `calc_hp_max`()
  2. BEGIN
  3. -- 创建接收游标的变量
  4. DECLARE hp INT;
  5. -- 创建总数变量
  6. DECLARE hp_sum INT DEFAULT 0;
  7. -- 创建结束标志变量
  8. DECLARE done INT DEFAULT false;
  9. -- 定义游标
  10. DECLARE cur_hero CURSOR FOR SELECT hp_max FROM heros;
  11. -- 指定游标循环结束时的返回值
  12. DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = true;
  13. OPEN cur_hero;
  14. read_loop:LOOP
  15. FETCH cur_hero INTO hp;
  16. -- 判断游标的循环是否结束
  17. IF done THEN
  18. LEAVE read_loop;
  19. END IF;
  20. SET hp_sum = hp_sum + hp;
  21. END LOOP;
  22. CLOSE cur_hero;
  23. SELECT hp_sum;
  24. END

当游标溢出时(也就是当游标指向到最后一行数据后继续执行会报的错误),我们可以定义一个 continue 的事件,指定这个事件发生时修改变量 done 的值,以此来判断游标是否已经溢出,即:

  1. DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = true;

使用游标来解决一些常见的问题

总结

虽然在处理某些复杂的数据情况下,使用游标可以更灵活,但同时也会带来一些性能问题,比如在使用游标的过程中,会对数据行进行加锁,这样在业务并发量大的时候,不仅会影响业务之间的效率,还会消耗系统资源,造成内存不足,这是因为游标是在内存中进行的处理。如果有游标的替代方案,我们可以采用替代方案。

image.png