一、显式cursor
显式是相对与隐式cursor而言的,就是有一个明确的声明的cursor。显式游标的声明类似如下(详细的语法参加plsql ref doc ):
cursor cursor_name (parameter list) is select ...
游标从declare、open、fetch、close是一个完整的生命旅程。当然了一个这样的游标是可以被多次open进行使用的,显式cursor是静态cursor,她的作用域是全局的,但也必须明白,静态cursor也只有pl/sql代码才可以使用她。下面看一个简单的静态显式cursor的示例:
1 declare 2 cursor get_gsmno_cur (p_nettype in varchar2) is 3 select gsmno 4 from gsm_resource 5 where nettype=p_nettype and status='0'; 6 v_gsmno gsm_resource.gsmno%type; 7 begin 8 open get_gsmno_cur('139'); 9 loop 10 fetch get_gsmno_cur into v_gsmno; 11 exit when get_gsmno_cur%notfound; 12 dbms_output.put_line(v_gsmno); 13 end loop; 14 close emp_cur; 15 16 open get_gsmno_cur('138'); 17 loop 18 fetch get_gsmno_cur into v_gsmno; 19 exit when get_gsmno_cur%notfound; 20 dbms_output.put_line(v_gsmno); 21 end loop; 22 close get_gsmno_cur; 23 end;
上面这段匿名块用来实现选号的功能,我们显式的定义了一个get_gsmno_cur,然后根据不同的号段输出当前系统中该号短对应的可用手机号码。当然了,实际应用中没人这么用的,我只是用来说应一个显式cursor的用法。
二、隐式cursor
隐式cursor当然是相对于显式而言的,就是没有明确的cursor的declare。在Oracle的PL/SQL中,所有的DML操作都被Oracle内部解析为一个cursor名为SQL的隐式游标,只是对我们透明罢了。
另外,我们前面提到的一些循环操作中的指针for 循环,都是隐式cursor。
隐式cursor示例一:
CREATE TABLE zrp (str VARCHAR2(10)); insert into zrp values ('ABCDEFG'); insert into zrp values ('ABCXEFG'); insert into zrp values ('ABCYEFG'); insert into zrp values ('ABCDEFG'); insert into zrp values ('ABCZEFG'); COMMIT; SQL> begin 2 update zrp SET str = 'updateD' where str like '%D%'; 3 ifSQL%ROWCOUNT= 0 then 4 insert into zrp values ('1111111'); 5 end if; 6 end; 7 / PL/SQL procedure successfully completed SQL> select * from zrp; STR ---------- updateD ABCXEFG ABCYEFG updateD ABCZEFG SQL> SQL> begin 2 update zrp SET str = 'updateD' where str like '%S%'; 3 ifSQL%ROWCOUNT= 0 THEN 4 insert into zrp values ('0000000'); 5 end if; 6 end; 7 / PL/SQL procedure successfully completed SQL> select * from zrp; STR ---------- updateD ABCXEFG ABCYEFG updateD ABCZEFG 0000000 6 rows selected SQL>
隐式cursor示例二:
1 begin 2 for rec in (select gsmno,status from gsm_resource) loop 3 dbms_output.put_line(rec.gsmno||'--'||rec.status); 4 end loop; 5 end;
REF cursor
Ref cursor属于动态cursor(直到运行时才知道这条查询)。
从技术上讲,在最基本的层次静态cursor和ref cursor是相同的。一个典型的PL/SQL光标按定义是静态的。Ref光标正好相反,可以动态地打开,或者利用一组SQL静态语句来打开,选择哪种方法由逻辑确定(一个IF/THEN/ELSE代码块将打开一个或其它的查询)。例如,下面的代码块显示一个典型的静态SQL光标,光标C。此外,还显示了如何通过使用动态SQL或静态SQL来用ref光标(在本例中为L_CURSOR)来打开一个查询:
1,什么是 REF游标 ?
动态关联结果集的临时对象。即在运行的时候动态决定执行查询。
2,REF 游标 有什么作用?
实现在程序间传递结果集的功能,利用REF CURSOR也可以实现BULK SQL,从而提高SQL性能。
3,静态游标和REF 游标的区别是什么?
①静态游标是静态定义,REF 游标是动态关联;
②使用REF 游标需REF 游标变量。
③REF 游标能做为参数进行传递,而静态游标是不可能的。
4,什么是REF 游标变量?
REF游标变量是一种 引用 REF游标类型 的变量,指向动态关联的结果集。
5,怎么使用 REF游标 ?
①声明REF 游标类型,确定REF 游标类型;
⑴强类型REF游标:指定retrun type,REF 游标变量的类型必须和return type一致。
语法:Type REF游标名 IS Ref Cursor Return 结果集返回记录类型;
⑵弱类型REF游标:不指定return type,能和任何类型的CURSOR变量匹配,用于获取任何结果集。
语法:Type REF游标名 IS Ref Cursor;
②声明Ref 游标类型变量;
语法:变量名 已声明Ref 游标类型;
③打开REF游标,关联结果集 ;
语法:Open Ref 游标类型变量 For 查询语句返回结果集;
④获取记录,操作记录;
语法:Fatch REF游标名 InTo 临时记录类型变量或属性类型变量列表;
⑤关闭游标,完全释放资源;
语法:Close REF游标名;
1 Declare 2 type rc is ref cursor; 3 cursor c is select * from dual; 4 5 l_cursor rc; 6 begin 7 if (to_char(sysdate,'dd') = 30) then 8 -- ref cursor with dynamic sql 9 open l_cursor for 'select * from emp'; 10 elsif (to_char(sysdate,'dd') = 29) then 11 -- ref cursor with static sql 12 open l_cursor for select * from dept; 13 else 14 -- with ref cursor with static sql 15 open l_cursor for select * from dual; 16 end if; 17 -- the "normal" static cursor 18 open c; 19 end;
在这段代码块中,可以看到了最显而易见的区别:无论运行多少次该代码块,光标C总是select * from dual。相反,ref光标可以是任何结果集,因为"select * from emp"字符串可以用实际上包含任何查询的变量来代替。
在上面的代码中,声明了一个弱类型的REF cursor,下面再看一个强类型(受限)的REF cursor,这种类型的REF cursor在实际的应用系统中用的也是比较多的。
1 create table gsm_resource 2 ( 3 gsmno varchar2(11), 4 status varchar2(1), 5 price number(8,2), 6 store_id varchar2(32) 7 ); 8 insert into gsm_resource values('13905310001','0',200.00,'SD.JN.01'); 9 insert into gsm_resource values('13905312002','0',800.00,'SD.JN.02'); 10 insert into gsm_resource values('13905315005','1',500.00,'SD.JN.01'); 11 insert into gsm_resource values('13905316006','0',900.00,'SD.JN.03'); 12 commit; 13 14 SQL> declare 15 2 type gsm_rec is record( 16 3 gsmno varchar2(11), 17 4 status varchar2(1), 18 5 price number(8,2)); 19 6 20 7 type app_ref_cur_type is ref cursor return gsm_rec; 21 8 my_cur app_ref_cur_type; 22 9 my_rec gsm_rec; 23 10 24 11 begin 25 12 open my_cur for select gsmno,status,price 26 13 from gsm_resource 27 14 where store_id='SD.JN.01'; 28 15 fetch my_cur into my_rec; 29 16 while my_cur%found loop 30 17 dbms_output.put_line(my_rec.gsmno||'#'||my_rec.status||'#'||my_rec.price); 31 18 fetch my_cur into my_rec; 32 19 end loop; 33 20 close my_cur; 34 21 end; 35 22 / 36 37 13905310001#0#200 38 13905315005#1#500 39 40 PL/SQL procedure successfully completed 41 42 SQL>
例子:强类型REF游标
1 Declare 2 Type MyRefCurA IS REF CURSOR RETURN emp%RowType; 3 Type MyRefCurB IS REF CURSOR RETURN emp.ename%Type; 4 vRefCurA MyRefCurA; 5 vRefCurB MyRefCurB; 6 vTempA vRefCurA%RowType; 7 vTempB vRefCurB.ename%Type; 8 9 Begin 10 Open vRefCurA For Select * from emp Where SAL > 2000; 11 Loop 12 Fatch vRefCurA InTo vTempA; 13 Exit When vRefCurA%NotFound; 14 DBMS_OUTPUT.PUT_LINE(vRefCurA%RowCount||' '|| vTempA.eno||' '||vTempA.ename ||' '||vTempA.sal) 15 End Loop; 16 Close vRefCurA; 17 18 DBMS_OUTPUT.PUT_LINE('-------------------------------------------------------------------------------------------------------'); 19 20 Open vRefCurB For Select ename from emp Where SAL > 2000; 21 Loop 22 Fatch vRefCurB InTo vTempB; 23 Exit When vRefCurB%NotFound; 24 DBMS_OUTPUT.PUT_LINE(vRefCurB%RowCount||' '||vTempB) 25 End Loop; 26 Close vRefCurB; 27 28 DBMS_OUTPUT.PUT_LINE('-------------------------------------------------------------------------------------------------------'); 29 30 Open vRefCurA For Select * from emp Where JOB = 'CLERK'; 31 Loop 32 Fatch vRefCurA InTo vTempA; 33 Exit When vRefCurA%NotFound; 34 DBMS_OUTPUT.PUT_LINE(vRefCurA%RowCount||' '|| vTempA.eno||' '||vTempA.ename ||' '||vTempA.sal) 35 End Loop; 36 Close vRefCurA; 37 End;
例子:弱类型REF游标
1 Declare 2 Type MyRefCur IS Ref Cursor; 3 vRefCur MyRefCur; 4 vtemp vRefCur%RowType; 5 Begin 6 Case(&n) 7 When 1 Then Open vRefCur For Select * from emp; 8 When 2 Then Open vRefCur For Select * from dept; 9 Else 10 Open vRefCur For Select eno, ename from emp Where JOB = 'CLERK'; 11 End Case; 12 Close vRefCur; 13 End;
6,怎样让REF游标作为参数传递?
--作为函数返回值
1 create or replace function returnacursor return sys_refcursor 2 is 3 v_csr sys_refcursor; 4 begin 5 open v_csr for select a1 from test3; 6 return v_csr; 7 end; 8 9 10 declare 11 c sys_refcursor; 12 a1 char(2); 13 begin 14 c:=returnacursor; 15 loop 16 fetch c into a1; 17 exit when c%notfound; 18 dbms_output.put_line(a1); 19 end loop; 20 close c; 21 end;
--作为参数
1 create or replace procedure proc_ref_cursor (rc in sys_refcursor) as 2 v_a number; 3 v_b varchar2(10); 4 5 begin 6 loop 7 fetch rc into v_a, v_b; 8 exit when rc%notfound; 9 dbms_output.put_line(v_a || ' ' || v_b); 10 end loop; 11 end; 12 13 14 declare 15 v_rc sys_refcursor; 16 begin 17 open v_rc for 18 select a1,a2 from test3; 19 proc_ref_cursor(v_rc); 20 close v_rc; 21 end;
普通cursor与REF cursor还有一些大家应该都熟悉的区别,我再浪费点唾沫。
1)PL/SQL静态光标不能返回到客户端,只有PL/SQL才能利用它。ref光标能够被返回到客户端,这就是从Oracle的存储过程返回结果集的方式。
2)PL/SQL静态光标可以是全局的,而ref光标则不是。 也就是说,不能在包说明或包体中的过程或函数之外定义ref光标。 只能在定义ref光标的过程中处理它,或返回到客户端应用程序。
3)ref光标可以从子例程传递到子例程,而光标则不能。 为了共享静态光标,必须在包说明或包体中把它定义为全局光标。 因为使用全局变量通常不是一种很好的编码习惯,因此可以用ref光标来共享PL/SQL中的光标,无需混合使用全局变量。
最后,使用静态光标--通过静态SQL(但不用ref光标)--比使用ref光标效率高,而ref光标的使用仅限于以下几种情况:
把结果集返回给客户端;
在多个子例程之间共享光标(实际上与上面提到的一点非常类似);
没有其他有效的方法来达到你的目标时,则使用ref光标,正如必须用动态SQL时那样;
简言之,首先考虑使用静态SQL,只有绝对必须使用ref光标时才使用ref光标,也有人建议尽量使用隐式游标,避免编写附加的游标控制代码(声明,打开,获取,关闭),也不需要声明变量来保存从游标中获取的数据。这个就因人因具体的case大家去酌定吧。
游标属性
%FOUND: bool - TRUE if >1 row returned
%NOTFOUND:bool - TRUE if 0 rows returned
%ISOPEN: bool - TRUE if cursor still open
%ROWCOUNT:int - number of rows affected by last SQL statement
注:NO_DATA_FOUND和%NOTFOUND的用法是有区别的,小结如下:
1)SELECT . . . INTO 语句触发 NO_DATA_FOUND;
2)当一个显式光标的 where 子句未找到时触发 %NOTFOUND;
3)当UPDATE或DELETE 语句的where 子句未找到时触发 SQL%NOTFOUND;
4)在光标的提取(Fetch)循环中要用 %NOTFOUND 或%FOUND 来确定循环的退出条件,不要用NO_DATA_FOUND。