1、CURSOR综述




2、申明游标


declare

      CURSER 游标名 IS 查询语句

注:如果在查询语句中使用了pl/sql变量,变量声明必须放在游标的前面



DECLARE

      v_major studengs.major%type;

       c_student

             SELECT irst_name,last_name

             FROM  students

             WHERE  major = v_major







PS:当打开游标的时候,就代表所定义的游标后面的查询语句真正的被执行,在打开游标的的时候变量一定要去初始化值,否则这个值就是一个空值,导致查询后面的where条件就失去了意义。最后把执行的查询结果返回到游标所定义的空间当中,并且在游标当中会提供一些操作方法来操作这些语句

注:如果游标对应的select语句使用了“FOR UPDATE”(行级排他锁)选项,则此游标锁定

说明:






 FETCH  FROM 


 fetch  游标名 

 fetch  游标名  INTO 







 游标使用后应该关闭【务必谨记

fetch 关闭后的游标 是非法的

关闭一个关闭了的游标也是非法的


游标所对应的内存工作区变为无效,释放与游标相关的系统资源



declare
       v_id  number(4);
       CURSOR  my_cur  IS --声明游标
               select  first_name,salary,start_date  from  s_emp  where  id = v_id;
       v_fn  s_emp.first_name%type;     --定义三个变量用以接收游标里面查询出来的参数
       v_sal  s_emp.salary%type;
       v_sd  s_emp.start_date%type;
begin
     v_id := &id--根据输入的id号码来查询出所对应的结果值
     open  my_cur;    --打开游标
     fetch  my_cur  into  v_fn,v_sal,v_sd;   --检索游标
     --打印输出结果
     dbms_output.put_line('id:' || v_id || '  name:' || v_fn || ' salary:' || v_sal || ' start_date:' || v_sd );
     close my_cur;       --关闭游标(必须的)
end;




6、游标的属性

 %FOUND 如果fetch到数据。则返回true,否则返回false

 %NOT FOUND  如果fetch不到数据,则返回true,否则返回false

 %ISOPEN  如果当前游标已打开,则返回true

 %ROWCOUNT 返回当前游标的指针位移量,当然,游标一定要处于打开状态

7、游标的fetch循环




LOOP

       游标名  INTO ……  --提取数据

      EXIT  游标名%NOTFOUND;--退出条件

END  --结束循环

close 游标名 --关闭游标






while 游标名%FOUND  LOOP

      fetch  游标名 

END 

close 游标名 –关闭游标

--注意:在使用while的时候,在while之前一定要先fetch一下先,否则进不了循环体,因为开始是空的






FOR  var  IN  cursor  LOOP

……

END LOOP

注:在for循环中,会自动打开,自动fetch,和自动关闭游标,所以,使用for循环是最为简便的方法



8、一个游标的具体实例:

     

方法一:分别定义变量和游标



declare
       v_fName varchar2(128);        --定义三个变量来存放数据
       v_title s_emp.title%type;
       v_startDate s_emp.start_date%type;
       CURSOR  my_cur  IS        --声明一个游标
       select  first_name|| ' '||last_name,title,start_date  from  s_emp  where  salary<1500;
begin
     open  my_cur;
     loop
       fetch  my_cur  into  v_fName,v_title,v_startDate;
       dbms_output.put_line(v_fName|| ', ' || v_title ||', ' || v_startDate );  --输出结果
       exit when my_cur%notfound;  --跳出循环的条件
     end loop;
     close my_cur;       --关闭
end;



方法二:改写方法一

下面是这个例子的第二中写法:不需要很麻烦的定义三个变量来存放数据,可以直接定义一个游标型的变量来存放游标里面存放的所有的数据



declare
       --v_fName varchar2(128);
       --v_title s_emp.title%type;
       --v_startDate s_emp.start_date%type;
       CURSOR  my_cur  IS
       select  first_name|| ' '||last_name n,title,start_date sd  from  s_emp  where 

             --为了方便起见,我们给上面查询到的字段起了一个别名
       emp  my_cur%rowtype;--直接定义一个游标类型一样的变量,用来存放所有的数据
begin
     open  my_cur;
     loop
       fetch  my_cur  into  emp;--直接把数据给扔到上面定义的变量里面
       dbms_output.put_line(emp.n|| ', ' || emp.title ||', ' || emp.sd );
       exit when my_cur%notfound;
     end loop;
     close my_cur;
end;



方法三:直接使用for循环


其实上面括号里面的红色部分就是一个游标,由此可以看出,当使用for循环的时候,是最简便的方法

在for循环中,变量因子无需声明,游标也无需显式的打开和关闭,并且也不需要显式的去fetch数据。

当然红色的部分也可以在DECLARE里面事先定义好

9、for循环检索游标的特性






10、带参数的游标



DECLARE

      cursor c_student(p_major  students.major%typeIS

      select * from student where najor = p_major;

BEGIN

      OPEN  c_student(101); --传入参数

     



注意:

 定义参数化游标时,只能指定参数的类型,而不能指定参数的长度、精度、刻度,比如VARCHAR2(20),只能是VARCHAR2,后面的不能再有括号里面的数据


11、利用游标更新或删除数据


cursor 游标名 IS

 select语句 

【OF 列名】【NOWAIT】







更新或是修改的语法为

     

where current of 游标名

注意:

            

12、案例

修改员工的工资,如果员工的部门号为10,则工资提高100;如果部门号为20,则工资提高150;如果部门号为30,则工资提高200;否则工资提高250.



declare
       cursor c_emp is  select  *  from  emp  for update;
       v_increment  number;
begin
     for  v_emp  in  c_emp  loop
          case  v_emp.empno
            when  10  then  v_increment := 100;
            when  20  then  v_increment := 150;
            when  30  then  v_increment := 200;
            else  v_increment := 250;
          end  case;
          update  emp  set  sal=sal + v_increment  where  current  of  c_emp;
     end loop;
     commit;              --注意要提交事务
end;



13、隐式游标










修改员工号为1000的员工的工资,将他的工资增加100.如果该员工不存在,则向emp表中插入一个员工号为1000,工资为1600的员工

方法一:



begin
    update emp set sal=sal+100  where  empno=1000;
    if  sql%NOTFOUND  then
       INSERT  INTO  emp(empno,sal) values(1000,1600);
    end if;
    commit;
end;



            



begin
    update emp set sal=sal+100  where  empno=1000;
    if  sql%rowcount = 0  then
       INSERT  INTO  emp(empno,sal) values(1000,1600);
    end if;
    commit;
end;



14、游标变量


游标变量是一个指向多行查询结果集的指针,不与特定的查询绑定,因此具有非常大的灵活性,可以在打开游标变量时定义查询,可以返回不同结构的结果集







 REF 


 IS  REF  CURSOR  RETURN  type

type 可以是任务合法的Oracle数据类型


 IS  REF 


002、一个强类型的例子



declare 
        
 strong_cursor is  ref  cursor  return  s_emp%rowtype;--这是一个强类型          my_c1  strong_cursor;          emp  s_emp%rowtype;  --由于这是一个强类型,所以只能是跟游标类型相对应的变量          begin
              open 
 my_c1 for  select * from  s_emp dept_id=41;
             
 dbms_output.put_line('----------输出41部门的员工----------------');               loop
                  fetch  my_c1  into 
 emp;
                  exit  when 
 my_c1%notfound;
                
  dbms_output.put_line(emp.id||''||emp.salary);               end  loop;
              close  my_c1;
             
              open 
 my_c1 for  select * from  s_emp where  salary>1000;
          
    dbms_output.put_line('----------输出工资大于1000的员工 -------------');               loop
                  fetch 
 my_c1 into  emp;
                  exit 
  my_c1%notfound;
                
  dbms_output.put_line(emp.id||''||emp.salary);
             end  loop;
       



由上面的例子可以看出,使用游标变量使得查询等变的很随意

003、一个若类型的例子


004、sys_refcursor类型



就像下面一个例子



declare
     type id_list is  table  of  integer INDEX BY binary_integer;
     type  name_list  is  table  of  varchar2(200) INDEX BY binary_integer;
     ids  id_list;
     names  name_list;
     my_cur  sys_refcursor;
begin
     open my_cur  for  select  id, first_name from  s_emp;
     fetch  my_cur  bulk  collect  INTO ids, names;
     close my_cur;
     for i in  ids.first..ids.last  loop
         dbms_output.put_line('ID = '||ids(i)||'NAME = '||names(i));
     end  loop;
end;