免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
论坛 数据库技术 MySQL my
最近访问板块 发新帖
查看: 1682 | 回复: 3
打印 上一主题 下一主题

my [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-10-20 18:16 |只看该作者 |倒序浏览
Mysql的游标究竟怎么用--映日荷花别样红


Mysql从5.0开始支持存储过程和trigger,给我们喜欢用mysql的朋友们更喜欢mysql的理由了,语法
上和PL/SQL有差别,不过搞过编程的人都知道,语法不是问题,关键是思想,大致了解语法后,就从
变量定义,循环,判断,游标,异常处理这个几个方面详细学习了。关于游标的用法Mysql现在提供
的还很特别,虽然使用起来没有PL/SQL那么顺手,不过使用上大致上还是一样,
定义游标
declare fetchSeqCursor cursor for select seqname, value from sys_sequence;
使用游标
open fetchSeqCursor;
fetch数据
fetch cursor into _seqname, _value;
关闭游标
close fetchSeqCursor;
不过这都是针对cursor的操作而已,和PL/SQL没有什么区别吧,不过光是了解到这个是根本不足以
写出Mysql的fetch过程的,还要了解其他的更深入的知识,我们才能真正的写出好的游标使用的proc
edure

首先fetch离不开循环语句,那么先了解一下循环吧。
我一般使用Loop和while觉得比较清楚,而且代码简单。
这里使用Loop为例
fetchSeqLoop:Loop
fetch cursor into _seqname, _value;
end Loop;
现在是死循环,还没有退出的条件,那么在这里和oracle有区别,Oracle的PL/SQL的指针有个隐性变
量%notfound,Mysql是通过一个Error handler的声明来进行判断的,
declare continue handler for Not found (do some action);
在Mysql里当游标遍历溢出时,会出现一个预定义的NOT FOUND的Error,我们处理这个Error并定义一
个continue的handler就可以叻,关于Mysql Error handler可以查询Mysql手册
定义一个flag,在NOT FOUND,标示Flag,在Loop里以这个flag为结束循环的判断就可以叻。

declare fetchSeqOk boolean; ## define the flag for loop judgement
declare _seqname varchar(50); ## define the varient for store the data
declare _value bigint(20);
declare fetchSeqCursor cursor for select seqname, value from sys_sequence;## define the cursor
declare continue handler for NOT FOUND set fetchSeqOk = true; ## define the continue handler for not
found flag
set fetchSeqOk = false;

open fetchSeqCursor;
fetchSeqLoop:Loop
if fetchSeqOk then
  leave fetchSeqLoop;
else
  fetch cursor into _seqname, _value;
  select _seqname, _value;
end if;
end Loop;
close fetchSeqCursor;

这就是一个完整的过程叻,那么会思考的人一般在这里都会思考,如果是这样的话,怎样做嵌套的游
标循环叻,这里可以根据statement block的scope实现叻,Mysql里通过begin end来划分一个statem
ent block,在block里定义的变量范围也在这个block里,所以关于嵌套的游标循环我们可以多加一
个begin end来区分他们所对应的error handler(注意在Mysql里同一个error的handler只能定义一次
,多定义的话,在compile的过程中会提示里duplicate handler defination,所以NOT FOUND的hand
ler就只能定义一次),在一个begin end里定义这个里面游标的NOT FOUND handler,

declare fetchSeqOk boolean; ## define the flag for loop judgement
declare _seqname varchar(50); ## define the varient for store the data
declare _value bigint(20);
declare fetchSeqCursor cursor for select seqname, value from sys_sequence;## define the cursor
declare continue handler for NOT FOUND set fetchSeqOk = true; ## define the continue handler for not
found flag
set fetchSeqOk = false;

open fetchSeqCursor;
fetchSeqLoop:Loop
if fetchSeqOk then
  leave fetchSeqLoop;
else
  fetch cursor into _seqname, _value;  
  begin
   declare fetchSeqOk boolean default 'inner';
   declare cursor2 cursor for select .... from ...;## define the cursor
   declare continue handler for NOT FOUND set fetchSeqOk = true; ## define the continue handler for n
ot
   set fetchSeqOk = false;
   open cursor2;
   fetchloop2 loop
    if fetchSeqOk then
    else
    end if;
   end loop;
   close cursor2;
  end;
end if;
end Loop;
close fetchSeqCursor;

这样就可以轻松实现更多层次的循环了,不过相对oracle的PL/SQL来说,Mysql现在还不支持动态游
标的定义,所以很强大的动态拼出SQL的在游标里还不能做到,不过这完全不影响我对Mysql的喜爱程
度,她就想那羞涩的荷花一样,虽然没有灿烂的色彩,但那简约的色调,清新而不染一丝铅尘的高雅
,一样吸引着无数的mysql迷么,正如接天莲叶无穷碧,映日荷花别样红。

付:Mysql也有类似Oracle里的execute immediate的动态SQL的功能,通过这个功能可有多少弥补一
些动态游标的缺憾叻
set @sqlStr='select * from table where condition1 = ?';
prepare s1 for @sqlStr;
execute s1 using @condition1; 如果有多个参数用逗号分隔
deallocate prepare s1; 手工释放,或者是connection关闭时,server自动回收。



--------------------------------------------------------

比较基础的
可以用在存储过程的SQL语句主要有以下类型:

1、 无返回结果语句,如:INSERT,UPDATE,DROP, DELETE等
2、 select语句返回单行变量并可传给本地变量(select ..into)
3、 返回多行结果集的select语句,并可使用游标循环处理

注意,存储过程返回的多行结果集,可以被客户端程序(如php)所接收,但要在一个存储过程中接收另一个存储过程的结果集是不可能的,一般解决办法是存入临时表供其它过程共用

4、 prepare语句

以下主要讲述游标及prepare部分

游标

定义

DECLARE cursor_name CURSOR FOR SELECT_statement;


游标操作
OPEN 打开游标
    OPEN cursor_name;

FETCH 获取游标当前指针的记录,并传给指定变量列表,注意变量数必须与游标返回的字段数一致,要获得多行数据,使用循环语句去执行FETCH
    FETCH cursor_name INTO variable list;


CLOSE关闭游标

CLOSE cursor_name ;

注意:mysql的游标是向前只读的,也就是说,你只能顺序地从开始往后读取结果集,不能从后往前,也不能直接跳到中间的记录.

一个完整的例子:

   -- 定义本地变量

    DECLARE o varchar(12;


   -- 定义游标

   DECLARE ordernumbers CURSOR

   FOR

   SELECT callee_name FROM account_tbl where acct_timeduration=10800;

   DECLARE CONTINUE HANDLER FOR NOT FOUND SET no_more_departments=1;

   SET no_more_departments=0;

   
   -- 打开游标

   OPEN ordernumbers;


   -- 循环所有的行

   REPEAT

       -- Get order number

      FETCH ordernumbers INTO o;

      update account set allMoney=allMoney+72,lastMonthConsume=lastMonthConsume-72 where NumTg=@o;
     
     
  -- 循环结束

      UNTIL no_more_departments
     END REPEAT;

   -- 关闭游标

   CLOSE ordernumbers;

论坛徽章:
0
2 [报告]
发表于 2009-10-20 18:18 |只看该作者
create procedure test(out d float)
begin
declare a,b int;
declare cur_1 cursor for select count(*) from sur_question;
DECLARE CONTINUE HANDLER for not found
set b = 1;
open cur_1;
  repeat
    fetch cur_1 into a;
    until b = 1
  end repeat;
close cur_1;
set d = a;
end



CREATE PROCEDURE p (OUT return_val INT)
BEGIN
DECLARE a,b INT;
DECLARE cur_1 CURSOR FOR SELECT count(*) from sur_question;
DECLARE CONTINUE HANDLER FOR NOT FOUND
SET b = 1;
OPEN cur_1;
REPEAT
FETCH cur_1 INTO a;
UNTIL b = 1
END REPEAT;
CLOSE cur_1;
SET return_val = a;
END;


/*初始化*/  
drop procedure if exists  useCursor //   

/*建立 存储过程 create */  
CREATE PROCEDURE useCursor()
    BEGIN
    /*局部变量的定义 declare*/  
         declare tmpName varchar(20) default '' ;
         declare allName varchar(255) default '' ;
         
         declare cur1 CURSOR FOR SELECT name FROM test.level ;
         
         /*    mysql 不知道为什么用异常加入判断 ?
          *    此请参考官方文档 20.2.11. 光标 光标  
          *        这把 游标 异常后 捕捉  
          *        并设置 循环使用 变量 tmpname 为 null 跳出循环。
          */
         declare CONTINUE HANDLER FOR SQLSTATE '02000' SET tmpname = null;
   
   
    /*开游标*/  
     OPEN cur1;
         /*游标向下走一步*/  
         FETCH cur1 INTO tmpName;
         
         /* 循环体 这很明显 把游标查询出的 name 都加起并用 ; 号隔开 */
      WHILE ( tmpname is not null) DO
          set tmpName = CONCAT(tmpName ,";") ;
          set allName = CONCAT(allName ,tmpName) ;
        /*游标向下走一步*/  
        FETCH cur1 INTO tmpName;
      END WHILE;
   
    CLOSE cur1;
   
    select allName ;
END;//
call useCursor()//


运行结果:
mysql> call useCursor()//
+--------------------------------------+
| allName                              |
+--------------------------------------+
| f1;c3;c6;c5;c2;c4;c1;f1;f3;f4;f2;f5; |
+--------------------------------------+
1 row in set (0.00 sec)

论坛徽章:
0
3 [报告]
发表于 2009-10-20 18:40 |只看该作者
就因为不知道怎么在mysql里找到一个类似oracle的%notfound的隐形定义。半年没动过mysql的cursor,想起这点特别恨mysql。。

多谢楼主。。

论坛徽章:
0
4 [报告]
发表于 2009-10-20 18:51 |只看该作者
CREATE PROCEDURE p25 (OUT return_val INT)
BEGIN
DECLARE a,b INT; <--
DECLARE cur_1 CURSOR FOR SELECT s1 FROM t;
DECLARE CONTINUE HANDLER FOR NOT FOUND
SET b = 1;
OPEN cur_1;
REPEAT
FETCH cur_1 INTO a;
UNTIL b = 1
END REPEAT;
CLOSE cur_1;
SET return_val = a;
END;//

这个过程开始声明了三个变量。附带说一下,顺序是十分重要的。首先要进行变量声明,
然后声明条件,随后声明游标,再后面才是声明错误处理器。如果你没有按顺序声明,
系统会提示错误信息。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP