mysql 游标详解_mysql中游标的使用案例详解(学习笔记)(转)

1.游标是啥玩意?

简单的说:游标(cursor)就是游动的标识,啥意思呢,通俗的这么说,一条sql取出对应n条结果资源的接口/句柄,就是游标,沿着游标可以一次取出一行。我给大家准备一张图:

779bff6c0647499aba758a39ce77b24d.png

2.怎么使用游标?

//1.声明/定义一个游标

declare 声明;declare 游标名 cursor for select_statement;

//2.打开一个游标

open 打开;open 游标名

//3.取值

fetch 取值;fetch 游标名 into var1,var2[,...]

//4.关闭一个游标

close 关闭;close 游标名;

3.游标实战

未使用游标:

create procedure p1()

begin

select * from category;

end$

call p1$

执行结果:

c072bbb9067b4aa9d027903de1d481cf.png

使用游标:

/**

注释

*/

create procedure p2()

begin

//一下定义的三个变量用于将fetch取值出来的值放到对应的变量中

declare row_cat_id int;

declare row_cat_name int;

declare row_parent_id int;

//定义游标

declare getcategory cursor for select cat_id,cat_name,parent_id from category;

//打开游标

open getcategory;

//取值

fetch getcategory into row_cat_id,row_cat_name,row_parent_id;

//关闭游标

close getcategory;

end$

/**

未注释

*/

create procedure p2()

begin

declare row_cat_id int;

declare row_cat_name varchar(90);

declare row_parent_id int;

declare getcategory cursor for select cat_id,cat_name,parent_id from category;

open getcategory;

fetch getcategory into row_cat_id,row_cat_name,row_parent_id;

close getcategory;

end$

//执行的时候你会发现是0行,这时因为我们将查询出的结果赋给了变量,我们有没有对赋值后的变量进行查询显示。所以是0行。因此,我们要重新改进。

call p2()$

执行结果为:

aec8bcf66aa25f7fce01747dead960de.png

//改进

//删除游标重新执行

drop procedure p2$

create procedure p2()

begin

declare row_cat_id int;

declare row_cat_name varchar(90);

declare row_parent_id int;

declare getcategory cursor for select cat_id,cat_name,parent_id from category;

open getcategory;

fetch getcategory into row_cat_id,row_cat_name,row_parent_id;

select row_cat_id,row_cat_name,row_parent_id;

close getcategory;

end$

call p2()$

执行结果如下:

26de7fefa5a24d66dc4873c178f430d8.png

这时候你会发现我们只得到了一个查询结果,这时为什么呢?这时因为控制权在我们这里,我愿意取一行就一行,愿意取两行就两行。因此,我在把刚才的动作变一下。

create procedure p3()

begin

declare row_cat_id int;

declare row_cat_name varchar(90);

declare row_parent_id int;

declare getcategory cursor for select cat_id,cat_name,parent_id from category;

open getcategory;

fetch getcategory into row_cat_id,row_cat_name,row_parent_id;

select row_cat_id,row_cat_name,row_parent_id;

fetch getcategory into row_cat_id,row_cat_name,row_parent_id;

select row_cat_id,row_cat_name,row_parent_id;

fetch getcategory into row_cat_id,row_cat_name,row_parent_id;

select row_cat_id,row_cat_name,row_parent_id;

fetch getcategory into row_cat_id,row_cat_name,row_parent_id;

select row_cat_id,row_cat_name,row_parent_id;

fetch getcategory into row_cat_id,row_cat_name,row_parent_id;

select row_cat_id,row_cat_name,row_parent_id;

fetch getcategory into row_cat_id,row_cat_name,row_parent_id;

select row_cat_id,row_cat_name,row_parent_id;

close getcategory;

end$

我fetch六次,查询五次,这时候我们会得到什么呢?试一下嘛!

call p4()$

执行结果如下:

551fefc71e61769a933381b1232d6fe8.png

提示:发现什么了吗?相同的语句,我们每取一次就往后游一次,有几次就游几次,直到你把游完所有标识,这时候系统就会报【02000】这个错误,告诉我们游标已经走完了。我们这里游了六次,因此会打印前六条记录。

所以啊,我们如何循环游标来取出所有行?

思路:

1.计算所有行select count(*)

create procedure p4()

begin

declare row_cat_id int;

declare row_cat_name varchar(90);

declare row_parent_id int;

declare cnt int default 0;//定义总行数

declare i int default 0;

declare getcategory cursor for select cat_id,cat_name,parent_id from category;

select count(*) into cnt from category;//计算得出的总行数查询后赋给cnt变量

open getcategory;

repeat

set i:=i+1;

fetch getcategory into row_cat_id,row_cat_name,row_parent_id;

select row_cat_id,row_cat_name,row_parent_id;

until i>=cnt end repeat;

close getcategory;

end$

call p4()$

执行结果为:

ac8b1bc409831e0f474200c42bc78d32.png

由此可见已经一条条得到表中结果,再次强调游标在此处的意义在于它把取出每一行的权利交给了你,你可以在每取出这一行的repeat中再做其他判断。

2.给游标定义一个越界的标识

//在mysql游标(cursor)中,可以定义continue handler来操作一个越界标识,使用语法:declare continue handler for NOT FOUND statemet(当没数据的时候要执行的语句)

//这句话的意思是说,我要声明一个句柄事件,你往后取,一旦发生NOT FOUND 事件就会出发set ergodic:=0这个语句

create procedure p5()

begin

declare row_cat_id int;

declare row_cat_name varchar(90);

declare row_parent_id int;

declare ergodic int default 1;//声明一个变量表明还有数据可遍历

declare getcategory cursor for select cat_id,cat_name,parent_id from category;

declare continue handler for NOT FOUND set ergodic:=0;

open getcategory;

repeat

fetch getcategory into row_cat_id,row_cat_name,row_parent_id;

select row_cat_id,row_cat_name,row_parent_id;

until ergodic=0 end repeat;

close getcategory;

end$

call p5()$

执行结果为:

364eb8df9f910952eb83b515d948c557.png

发现问题没有?为啥第最后一个查了两次?这是什么原因?我们不妨来分析一下我们写的语句:

f8d1018ba5230327328adffe041ae748.png

既然问题已经分析出来后,我们如何处理这个问题呢?

解决方案:声明处理的hanlder不再是continue,而是exit即可达到目的。即:declare exit handler for NOT FOUND set ergodic:=0;

//exit与continue的区别是:exit触发后,后面的语句不再执行,而continue还需要继续执行。

注意:除了这exit与continue两种方式外,还有一种方式就是undo handler。

//采用undo handler方式触发后,前面的语句直接撤销。【但目前好像这种方式,mysql还不支持】

create procedure p6()

begin

declare row_cat_id int;

declare row_cat_name varchar(90);

declare row_parent_id int;

declare ergodic int default 1;

declare getcategory cursor for select cat_id,cat_name,parent_id from category;

declare exit handler for NOT FOUND set ergodic:=0;

open getcategory;

repeat

fetch getcategory into row_cat_id,row_cat_name,row_parent_id;

select row_cat_id,row_cat_name,row_parent_id;

until ergodic=0 end repeat;

close getcategory;

end$

call p6()$

执行结果为:

6681da7213d66f2dee9a98a21ecc1f04.png

由此,问题解决。

题外话:如果我们还是使用continue的方式去实现不重复的话,我们应该怎么做呢?这时候我们可以在我们代码逻辑上处理这种问题,我们先来分析一下代码:

提示:

你有没有考虑过,你第一次fetch取值的时候会不会存在没有数据(值为空)的情况,因此我们可以先手动的fetch一行出来,紧接着repeat下面的数据。

create procedure p7()

begin

declare row_cat_id int;

declare row_cat_name varchar(90);

declare row_parent_id int;

declare ergodic int default 1;

declare getcategory cursor for select cat_id,cat_name,parent_id from category;

declare continue handler for NOT FOUND set ergodic:=0;

open getcategory;

fetch getcategory into row_cat_id,row_cat_name,row_parent_id;

repeat

select row_cat_id,row_cat_name,row_parent_id;

fetch getcategory into row_cat_id,row_cat_name,row_parent_id;

until ergodic=0 end repeat;

close getcategory;

end$

call p7()$

执行结果为:

694fe29d4ba69074b5dcb1c7c73b45ad.png

附件:

测试数据库与数据表:

create table category (

cat_id smallint unsigned auto_increment primary key,

cat_name varchar(90) not null default '',

parent_id smallint unsigned

)engine myisam charset utf8;

INSERT INTO `category` VALUES

(1,'手机类型',0),

(2,'CDMA手机',1),

(3,'GSM手机',1),

(4,'3G手机',1),

(5,'双模手机',1),

(6,'手机配件',0),

(7,'充电器',6),

(8,'耳机',6),

(9,'电池',6),

(11,'读卡器和内存卡',6),

(12,'充值卡',0),

(13,'小灵通/固话充值卡',12),

(14,'移动手机充值卡',12),

(15,'联通手机充值卡',12);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值