一、游标简介
1、游标简介
游标是一个存储在MySQL服务器上的数据库查询,它不是一条select语句,而是被该语句检索出来的结果集。有了游标可以方便的对该结果集进行逐行处理。 游标的设计是一种数据缓冲区的思想,用来存放SQL语句执行的结果。游标是一种能从包括多条数据记录的结果集中每次提取一条记录的机制。
尽管游标能遍历结果中的所有行,但一次只指向一行。
2、游标的特性
游标具有三个属性:
A、不敏感(Asensitive):数据库可以选择不复制结果集
B、只读(Read only)
C、不滚动(Nonscrollable):游标只能向一个方向前进,并且不可以跳过任何一行数据。
3、游标的优点
游标是针对行操作的,对从数据库中SELECT查询得到的结果集的每一行可以进行分开的独立的相同或不同的操作,是一种分离的思想。游标是面向集合与面向行的设计思想之间的一种桥梁。
4、游标的缺点
游标的主要缺点是性能不高。
游标的开销与游标中进行的操作相关,如果在游标中进行复杂的操作,开销会非常高。如果采用面向集合的SQL语句,扫描成本为O(N);但如果采用面向集合的SQL语句的扫描成本为O(N*N),则使用游标有可能会带来性能上的提升。
游标的缺点是只能一行一行操作。在数据量大的情况下,速度过慢。数据库大部分是面对集合的,业务会比较复杂,而游标使用会有死锁,影响其他的业务操作,不可取。 当数据量大时,使用游标会造成内存不足现象。
它的作用就是用于对查询数据库所返回的记录进行遍历,以便进行相应的操作。
5、游标的适用场景
MySQL数据库中,可以在存储过程、函数、触发器、事件中使用游标。
二、游标的操作
1、游标的定义
DECLARE cursor_name CURSOR FOR select_statement
2、打开游标
OPEN cursor_name;
3、取游标中的数据
FETCH cursor_name INTO var_name [, var_name]...
4、关闭游标
CLOSE cursor_name;
5、释放游标
DEALLOCATE cursor_name;
三、游标实例
1、创建一张游标的测试表
CREATE TABLE cursor_table
(
id INT ,
name VARCHAR(10),
age INT
)ENGINE=innoDB DEFAULT CHARSET=utf8;
insert into cursor_table values(1, '孙悟空', 500);
insert into cursor_table values(2, '猪八戒', 200);
insert into cursor_table values(3, '沙悟净', 100);
insert into cursor_table values(4, '唐僧', 20);
使用三种方式使用游标创建一个存储过程,统计年龄大于30的记录的数量。
DELIMITER :定义mysql结束符,默认情况下,delimiter是分号;。mysql一遇到分号,就要自动执行。但有时候,不希望MySQL这么做。可能输入较多的语句,且语句中包含有分号,并且多个语句作为一个整体来执行,就需要修改默认的分隔符
2、Loop循环
DELIMITER $$
DROP PROCEDURE IF EXISTS `getTotal`$$
CREATE DEFINER=`root`@`localhost` PROCEDURE `getTotal`()
BEGIN
DECLARE total INT;
##创建接收游标数据的变量
DECLARE sid INT;
DECLARE sname VARCHAR(10);
#创建总数变量
DECLARE sage INT;
#创建结束标志变量
DECLARE done INT DEFAULT FALSE;
#创建游标
DECLARE cur CURSOR FOR SELECT id,NAME,age FROM cursor_table WHERE age>30;
#指定游标循环结束时的返回值
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
#设置初始值
SET sage = 0;
SET total=0;
#打开游标
OPEN cur;
#开始循环游标里的数据
read_loop:LOOP
#根据游标当前指向的一条数据
FETCH cur INTO sid,sname,sage;
#判断游标的循环是否结束
IF done THEN
LEAVE read_loop; #跳出游标循环
END IF;
#获取一条数据时,将count值进行累加操作,这里可以做任意你想做的操作,
SET total = total + 1;
#结束游标循环
END LOOP;
#关闭游标
CLOSE cur;
#输出结果
SELECT total;
END$$
DELIMITER ;
#调用存储过程
call getTotal();
结果显示
3
3、While循环
DELIMITER $$
DROP PROCEDURE IF EXISTS `getTotal2`$$
CREATE DEFINER=`root`@`localhost` PROCEDURE `getTotal2`()
BEGIN
DECLARE total INT;
##创建接收游标数据的变量
DECLARE sid INT;
DECLARE sname VARCHAR(10);
#创建总数变量
DECLARE sage INT;
#创建结束标志变量
DECLARE done INT DEFAULT FALSE;
#创建游标
DECLARE cur CURSOR FOR SELECT id,NAME,age FROM cursor_table WHERE age>30;
#指定游标循环结束时的返回值
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
SET total = 0;
OPEN cur;
FETCH cur INTO sid, sname, sage;
WHILE(NOT done)
DO
SET total = total + 1;
FETCH cur INTO sid, sname, sage;
END WHILE;
CLOSE cur;
SELECT total;
END$$
DELIMITER ;
4、Repeat循环
DELIMITER $$
DROP PROCEDURE IF EXISTS `getTotal3`$$
CREATE DEFINER=`root`@`localhost` PROCEDURE `getTotal3`()
BEGIN
DECLARE total INT;
##创建接收游标数据的变量
DECLARE sid INT;
DECLARE sname VARCHAR(10);
#创建总数变量
DECLARE sage INT;
#创建结束标志变量
DECLARE done INT DEFAULT FALSE;
#创建游标
DECLARE cur CURSOR FOR SELECT id,NAME,age FROM cursor_table WHERE age > 30;
#指定游标循环结束时的返回值
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
SET total = 0;
OPEN cur;
REPEAT
FETCH cur INTO sid, sname, sage;
IF NOT done THEN
SET total = total + 1;
END IF;
UNTIL done END REPEAT;
CLOSE cur;
SELECT total;
END$$
DELIMITER ;
5、使用游标数据
第一个例子从游标中检索第一行:
create procedure processorders()
begin
declare o int;
declare ordernumbers cursor
for
select order_num from orders;
open ordernumbers;
-- 利用fetch检索出第一行的order_num存储到一个名为o的局部变量中。
fetch ordernumbers into o;
close ordernumbers;
end;
第二个例子检索游标中的所有行,从第一行到最后一行:
create procedure processorders()
begin
declare done boolean default 0;
declare o int;
declare ordernumbers cursor
for
select order_num from orders;
/*
这条语句定义了一个continue handler,它是在条件出现时被执行的代码。这里,它指出当
sqlstate '02000'出现时,set done = 1。sqlstate '02000'是一个未找到条件,当
repeate由于没有更多的行供循环而不能继续时,出现这个条件。
*/
declare continue handler for sqlstate '02000' set done = 1;
open ordernumbers;
-- 当done为真(非零)时结束循环。
repeat
fetch ordernumbers into o;
until done end repeat;
close ordernumbers;
end;
第三个例子对从游标中取出的数据进行某种实际的处理
DELIMITER $$
DROP PROCEDURE IF EXISTS `ordertotal`$$
CREATE PROCEDURE ordertotal (IN onnumber INT,IN taxable BOOLEAN,OUT ototal DECIMAL (8, 2)) -- comment '计算带税合计的存储过程'
BEGIN
-- 定义局部变量
DECLARE total DECIMAL (8, 2) ;
-- 声明税率
DECLARE taxrate INT DEFAULT 6 ;
-- 计算纳税收入
SELECT SUM(item_price * quantity) FROM orderitems WHERE order_num = onnumber INTO total ;
-- 存储值 :是否需要纳税,由你传的参数决定
IF taxable THEN
SELECT total + (total / 100 * taxrate) INTO total
END IF ;
-- 最后当然把结果赋给out的ototal
SELECT total INTO ototal ;
END$$
DELIMITER ;
create procedure processorders()
begin
declare done boolean default 0;
declare o int;
declare t decimal(8,2);
declare ordernumbers cursor
for
select order_num from orders;
declare continue handler for sqlstate '02000' set done = 1;
-- 创建一个表用来存放结果
create table if not exists ordertotals
(order_num int, total decimal(8,2));
open ordernumbers;
repeat
fetch ordernumbers into o;
-- ordertotal为在上一章创建的一个用来计算带税合计的存储过程
call ordertotal(o, 1, t);
insert into ordertotals(order_num, total)
values(o, t);
until done end repeat;
close ordernumbers;
end;
-- 此存储过程不返回数据,但它能创建和填充另一个表。
来源:oschina
链接:https://my.oschina.net/u/2331760/blog/4301136