
从前面的文章我们知道,MySQL检索操作返回一组称为结果集的行。这组返回的行都是与SQL语句相匹配的行(零行或多行)。使用简单的SELECT语句,例如,没有办法得到第一行、下一行或前10行,也不存在每次一行地处理所有行的简单方法(相对于成批地处理它们)。
本文摘录自:
Ben Forta《MySQL必知必会》
小孩子4919《MySQL是怎样使用的:从零蛋开始学习MySQL》


游标
游标概念
游标(CURSOR)是一个存储在MySQL服务器上的数据库查询,它不是一条SELECT语句,而是被该语句检索出来的结果集。在存储了游标之后,应用程序可以根据需要滚动或浏览其中的数据。
游标主要用于交互式应用,其中用户需要滚动屏幕上的数据,并对数据进行浏览或做出更改。
使用游标须知
使用游标涉及几个明确的步骤。
在能够使用游标前,必须声明(定义)它。这个过程实际上没有检索数据,它只是定义要使用的SELECT语句。
一旦声明后,必须打开游标以供使用。这个过程用前面定义的SELECT语句把数据实际检索出来。
对于填有数据的游标,根据需要取出(检索)各行。
在结束游标使用时,必须关闭游标。
在声明游标后,可根据需要频繁地打开和关闭游标。在游标打开后,可根据需要频繁地执行取操作。
使用游标
创建游标
在创建游标的时候,需要指定一下与游标关联的查询语句,语法如下:
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的游标就创建成功了。
打开和关闭游标
在创建完游标之后,我们需要手动打开和关闭游标,语法也简单:
/* 打开游标 */OPEN 游标名称;/* 关闭游标 */CLOSE 游标名称;
我们再来修改一下上边的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();


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 ;

CALL cursor_products();

(图片没有截全...总共14条记录)
遍历结束时的执行策略
SELECT COUNT(*) FROM products INTO record_count;
我们之所以要获取结果集中记录的条数,是因为我们需要一个结束循环的条件,当调用FETCH语句的次数与结果集中记录条数相等时就结束循环。
DECLARE CONTINUE HANDLER FOR NOT FOUND 处理语句;
只要我们在存储过程中写了这个语句,那么在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 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条记录)
DECLARE CONTINUE HANDLER FOR NOT FOUND SET not_done = 0;
not_done变量的值为1时表明遍历结果集的过程还没有结束,当FETCH语句无法获取更多记录时,就会触发一个事件,从而导致MySQL服务器主动调用上边的这个语句将not_done变量的值改为0。另外,我们把原先的WHILE语句替换成了LOOP语句,直接在LOOP语句的循环体中判断not_done变量的值,当它的值为0时就主动跳出循环。
小结


end




