暂无图片
暂无图片
1
暂无图片
暂无图片
暂无图片

MySQL之CURSOR游标

GrowthDBA 2021-10-12
1670

从前面的文章我们知道,MySQL检索操作返回一组称为结果集的行。这组返回的行都是与SQL语句相匹配的行(零行或多行)。使用简单的SELECT语句,例如,没有办法得到第一行、下一行或前10行,也不存在每次一行地处理所有行的简单方法(相对于成批地处理它们)。

有时,需要在检索出来的行中前进或后退一行或多行。所以,今天的主角CURSOR游标就闪亮登场啦。

本文摘录自:

  • Ben Forta《MySQL必知必会》

  • 小孩子4919《MySQL是怎样使用的:从零蛋开始学习MySQL》

游标

游标概念

游标(CURSOR)是一个存储在MySQL服务器上的数据库查询,它不是一条SELECT语句,而是被该语句检索出来的结果集。在存储了游标之后,应用程序可以根据需要滚动或浏览其中的数据。

游标主要用于交互式应用,其中用户需要滚动屏幕上的数据,并对数据进行浏览或做出更改。

小提示
只能用于存储过程:不像多数DBMS,MySQL游标只能用于存储过程(和函数)。

使用游标须知

使用游标涉及几个明确的步骤。

  • 在能够使用游标前,必须声明(定义)它。这个过程实际上没有检索数据,它只是定义要使用的SELECT语句。

  • 一旦声明后,必须打开游标以供使用。这个过程用前面定义的SELECT语句把数据实际检索出来。

  • 对于填有数据的游标,根据需要取出(检索)各行。

  • 在结束游标使用时,必须关闭游标。

在声明游标后,可根据需要频繁地打开和关闭游标。在游标打开后,可根据需要频繁地执行取操作。

使用游标

我们可以根据这个游标取出它对应记录的信息,随后再移动游标,让它指向下一条记录。上面的小提示中,我们知道了游标既可以用在函数中,也可以用在存储过程中,我们下边以存储过程为例来说明游标的使用方式,它的使用大致分成这么四个步骤:
1、创建游标
2、打开游标
3、通过游标访问记录
4、关闭游标

创建游标

在创建游标的时候,需要指定一下与游标关联的查询语句,语法如下:

DECLARE 游标名称 CURSOR FOR 查询语句;

下面我们就定义一个存储过程:

DELIMITER $$CREATE PROCEDURE cursor_products()BEGIN  DECLARE products_record_cursor CURSOR FOR SELECT vend_id, prod_name, prod_price FROM products;END $$DELIMITER ;

这样名叫products_record_cursor的游标就创建成功了。

小提示
1、这个存储过程并没有做很多事情,DECLARE语句用来定义和命名游标。存储过程处理完成后,游标就消失(因为它局限于存储过程)。
2、如果存储过程中也有声明局部变量的语句,创建游标的语句一定要放在局部变量声明后头。

打开和关闭游标

在创建完游标之后,我们需要手动打开和关闭游标,语法也简单:

/* 打开游标 */OPEN 游标名称;/* 关闭游标 */CLOSE 游标名称;
打开游标意味着执行查询语句,创建一个该查询语句得到的结果集关联起来的游标,关闭游标意味着会释放该游标相关的资源,所以一旦我们使用完了游标就要把它关闭掉。当然如果我们不显式的使用CLOSE语句关闭游标的话,在该存储过程的END语句执行完之后会自动关闭的。

我们再来修改一下上边的cursor_products存储过程:

DELIMITER $$CREATE PROCEDURE cursor_products()BEGIN  DECLARE products_record_cursor CURSOR FOR SELECT vend_id, prod_name, prod_price FROM products;  OPEN products_record_cursor;  CLOSE products_record_cursor;END $$DELIMITER ;

使用游标获取记录

在知道怎么打开和关闭游标之后,我们正式来说一下如何使用游标来获取结果集中的记录,获取记录的语句长这样:

FETCH 游标名 INTO 变量1, 变量2, ... 变量n

这个语句的意思就是把指定游标对应记录的各列的值依次赋值给INTO后边的各个变量。我们来继续改写一下cursor_products存储过程:

DELIMITER $$CREATE PROCEDURE cursor_products()BEGIN  DECLARE vend_id_value INT;  DECLARE prod_name_value CHAR(255);  DECLARE prod_price_value DECIMAL(8,2);  DECLARE products_record_cursor CURSOR FOR SELECT vend_id, prod_name, prod_price FROM products;  OPEN products_record_cursor;  FETCH products_record_cursor INTO vend_id_value, prod_name_value, prod_price_value;  SELECT vend_id_value, prod_name_value, prod_price_value;  CLOSE products_record_cursor;END $$DELIMITER ;

我们来调用一下这个存储过程:

CALL cursor_products();

products表里有14条记录

我们这里只取出了第一条?是的,如果想获取多条记录,那需要把FETCH语句放到循环语句中,我们再来修改一下cursor_products存储过程:
DELIMITER $$CREATE PROCEDURE cursor_products()BEGIN  DECLARE vend_id_value INT;  DECLARE prod_name_value CHAR(255);  DECLARE prod_price_value DECIMAL(8,2);  DECLARE record_count INT;  DECLARE i INT DEFAULT 0;  DECLARE products_record_cursor CURSOR FOR SELECT vend_id, prod_name, prod_price FROM products;  SELECT COUNT(*) FROM products INTO record_count;  OPEN products_record_cursor;  WHILE i < record_count DO    FETCH products_record_cursor INTO vend_id_value, prod_name_value, prod_price_value;    SELECT vend_id_value, prod_name_value, prod_price_value;    SET i = i + 1;  END WHILE;  CLOSE products_record_cursor;END $$DELIMITER ;

这次我们又多使用了两个变量,record_count表示products表中的记录行数,i表示当前游标对应的记录位置。每调用一次FETCH语句,游标就移动到下一条记录的位置。看一下调用效果:
CALL cursor_products();

(图片没有截全...总共14条记录)

这回就把products表中的记录全都遍历完了。

遍历结束时的执行策略

上边介绍的遍历方式需要我们首先获得查询语句结构集中记录的条数,也就是需要先执行下边这条语句:
SELECT COUNT(*) FROM products INTO record_count;

我们之所以要获取结果集中记录的条数,是因为我们需要一个结束循环的条件,当调用FETCH语句的次数与结果集中记录条数相等时就结束循环

其实在FETCH语句获取不到记录的时候会触发一个事件,从而我们可以得知所有的记录都被获取过了,然后我们就可以去主动的停止循环。MySQL中响应这个事件的语句如下:
DECLARE CONTINUE HANDLER FOR NOT FOUND 处理语句;

只要我们在存储过程中写了这个语句,那么在FETCH语句获取不到记录的时候,服务器就会执行我们填写的处理语句

小提示
处理语句可以是简单的一条语句,也可以是由BEGIN ... END包裹的多条语句。

我们接下来再来改写一下cursor_products存储过程并调用:

DELIMITER $$CREATE PROCEDURE cursor_products()BEGIN  DECLARE vend_id_value INT;  DECLARE prod_name_value CHAR(255);  DECLARE prod_price_value DECIMAL(8,2);  DECLARE not_done INT DEFAULT 1;  DECLARE products_record_cursor CURSOR FOR SELECT vend_id, prod_name, prod_price FROM products;  DECLARE CONTINUE HANDLER FOR NOT FOUND SET not_done = 0;  OPEN products_record_cursor;  flag: LOOP    FETCH products_record_cursor INTO vend_id_value, prod_name_value, prod_price_value;    IF not_done = 0 THEN      LEAVE flag;    END IF;    SELECT vend_id_value, prod_name_value, prod_price_value;  END LOOP flag;  CLOSE products_record_cursor;END $$DELIMITER ;CALL cursor_products();

(图片没有截全...总共14条记录)

我们声明了一个默认值为1的not_done变量和一个这样的语句:
DECLARE CONTINUE HANDLER FOR NOT FOUND SET not_done = 0;

not_done变量的值为1时表明遍历结果集的过程还没有结束,当FETCH语句无法获取更多记录时,就会触发一个事件,从而导致MySQL服务器主动调用上边的这个语句将not_done变量的值改为0。另外,我们把原先的WHILE语句替换成了LOOP语句,直接在LOOP语句的循环体中判断not_done变量的值,当它的值为0时就主动跳出循环。

小结

今天的内容比较简单,下面简单做一下总结:
1、游标(CURSOR)是一个存储在MySQL服务器上的数据库查询,它不是一条SELECT语句,而是被该语句检索出来的结果集(就像是针对查询结果的查询...有点绕,大家理解一下);
2、MySQL的游标只能用于存储过程和函数、创建游标的语句一定要放在局部变量声明后头;
3、游标的使用方式,它的使用大致分成这么四个步骤:①创建游标;②打开游标;③通过游标访问记录;④关闭游标(不显式使用CLOSE语句关闭游标的话,在该存储过程的END语句执行完之后会自动关闭);
4、FETCH语句获取不到记录的时候会触发一个事件:DECLARE CONTINUE HANDLERFORNOTFOUND 处理语句; 只要我们在存储过程中写了这个语句,那么在FETCH语句获取不到记录的时候,服务器就会执行我们填写的处理语句。
今天主要学习了MySQL的CURSOR游标,介绍了什么是游标以及如何使用游标,并且讲解了对游标结果进行循环以及遍历结束时的执行策略。今天的内容比较简单,实操部分大家下来多加练习。

end

文章转载自 GrowthDBA,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论