oracle数据库之cursor、refcursor及sys_refcursor区别

19 篇文章 0 订阅
7 篇文章 0 订阅

概图:
在这里插入图片描述

1 显式cursor

显式是相对与隐式cursor而言的,就是有一个明确的声明的cursor。
显式游标的声明类似如下 :

cursor cursor_name (parameter list) is select ... 

游标一个完整的生命周期:

declare->open->fetch->close
  • 游标是可以被多次open进行使用的
  • 显式cursor是静态cursor,作用域是全局的
  • 静态cursor也只有pl/sql代码才可以使用她

下面看一个简单的静态显式cursor的示例:

DECLARE
	CURSOR get_gsmno_cur IS
		SELECT gsmno
		FROM gsm_resource
		WHERE nettype = p_nettype
			AND status = '0';
	v_gsmno gsm_resource.gsmno%TYPE;
BEGIN
    // step 1
	OPEN get_gsmno_cur('139');
	LOOP
		FETCH get_gsmno_cur INTO v_gsmno;
		EXIT WHEN get_gsmno_cur%notfound;
		dbms_output.put_line(v_gsmno);
	END LOOP;
	CLOSE emp_cur;
 
    // step 2
	OPEN get_gsmno_cur('138');
	LOOP
		FETCH get_gsmno_cur INTO v_gsmno;
		EXIT WHEN get_gsmno_cur%notfound;
		dbms_output.put_line(v_gsmno);
	END LOOP;
	CLOSE get_gsmno_cur;
END;
/

上面这段显式的定义了一个get_gsmno_cur
然后,根据不同的号段输出当前系统中该号短对应的可用手机号码。

2 隐式cursor

隐式cursor是相对于显式而言的,没有明确的cursor的declare
在Oracle的PL/SQL中,所有的DML操作都被Oracle内部解析为:一个cursor名为SQL的隐式透明游标,
另外,一些循环操作中的指针for 循环,都是隐式cursor。

例 1

DROP TABLE zrp;
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;

// step 1
BEGIN
	UPDATE zrp
		SET str = 'updateD'
			WHERE str LIKE '%D%';
	IF SQL % ROWCOUNT = 0 THEN
		INSERT INTO zrp VALUES ('1111111');
	END IF;
END;
/

SELECT * FROM zrp;

// step 2
BEGIN
	UPDATE zrp
		SET str = 'updateD'
			WHERE str LIKE '%S%';
	IF SQL % ROWCOUNT = 0 THEN
		INSERT INTO zrp VALUES ('0000000');
	END IF;
END;
/

SELECT * FROM zrp;
STR 
updateD
ABCXEFG
ABCYEFG
updateD
ABCZEFG
0000000

例 2

BEGIN
	FOR rec IN (
		SELECT gsmno, status FROM gsm_resource
	)
	LOOP
		dbms_output.put_line(rec.gsmno || '--' || rec.status);
	END LOOP;
END;
/

3 ref cursor

ref cursor属于动态cursor(直到运行时才知道这条查询)。

从技术上讲,在最基本的层次,静态cursor和ref cursor是相同的。

  • PL/SQL cursor 按定义是静态的
  • ref cursor 正好相反,可以动态地打开,或者利用一组SQL静态语句来打开

选择哪种方法,由逻辑确定(一个IF/THEN/ELSE代码块将打开一个或其它的查询)。

例3
下面的代码块显示一个典型的静态SQL cursor,cursor c。
此外,还显示了如何通过使用动态SQL或静态SQL来用ref cursor (在本例中为L_CURSOR)来打开一个查询:

Declare 
      type rc is ref cursor; 
      cursor c is select * from dual;       
      l_cursor rc; 
    begin 
      if (to_char(sysdate,'dd') = 30) then 
          -- ref cursor with dynamic sql 
          open l_cursor for 'select * from emp'; 
      elsif (to_char(sysdate,'dd') = 29) then 
          -- ref cursor with static sql 
          open l_cursor for select * from dept; 
      else 
           -- with ref cursor with static sql 
           open l_cursor for select * from dual; 
      end if; 
      -- the "normal" static cursor 
      open c; 
    end; 
    / 

此段代码块 最显而易见的区别:

cursor :无论运行多少次该代码块,cursor c始终是select * from dual。

相反:

ref cursor :可以是任何结果集,因为"select * from emp"字符串可以用实际上包含任何查询的变量来代替。

代码中声明了一个弱类型的ref cursor,

例 4
下面再看一个强类型(受限)的ref cursor,这种类型的ref cursor在实际的应用系统中用的也是比较多的。

    create table gsm_resource 
    ( 
      gsmno varchar2(11), 
      status varchar2(1), 
      price number(8,2), 
      store_id varchar2(32) 
    ); 
    insert into gsm_resource values('13905310001','0',200.00,'SD.JN.01'); 
    insert into gsm_resource values('13905312002','0',800.00,'SD.JN.02'); 
    insert into gsm_resource values('13905315005','1',500.00,'SD.JN.01'); 
    insert into gsm_resource values('13905316006','0',900.00,'SD.JN.03'); 
    commit; 
    
   declare 
          type gsm_rec is record( 
                gsmno varchar2(11), 
                status varchar2(1), 
                 price number(8,2)
                 ); 

          type app_ref_cur_type is ref cursor return gsm_rec; 
          my_cur app_ref_cur_type; 
          my_rec gsm_rec; 
      
      begin 
         open my_cur for select gsmno,status,price from gsm_resource  where store_id='SD.JN.01'; 
         fetch my_cur into my_rec; 
         while my_cur%found loop 
              dbms_output.put_line(my_rec.gsmno||'#'||my_rec.status||'#'||my_rec.price); 
              fetch my_cur into my_rec; 
         end loop;         
         close my_cur; 
      end; 
      / 

4 普通cursor与ref cursor的区别

(1)返回值
PL/SQL静态cursor不能返回到客户端,只有PL/SQL才能利用它。
ref cursor 能够被返回到客户端,这就是从Oracle的存储过程返回结果集的方式。

(2)作用域
PL/SQL静态cursor可以是全局的,而ref cursor则不是。
也就是说,不能在包说明或包体中的过程或函数之外定义ref cursor。
只能在定义ref cursor 的过程中处理它,或返回到客户端应用程序。

ref cursor可以从子例程传递到子例程,而cursor则不能。
为了共享静态cursor,必须在包declare或包body中把它定义为全局cursor。
使用全局变量通常不是一种很好的编码习惯,因此,可以用ref cursor来共享PL/SQL中的cursor,无需混合使用全局变量。

(3)使用场景
使用静态cursor

  • 通过静态SQL(但不用ref cursor)
  • 比使用ref cursor效率高

ref cursor 使用仅限于以下几种情况:

  • 把结果集返回给客户端;
  • 在多个子例程之间共享cursor(实际上与上面提到的一点非常类似);
  • 没有其他有效的方法来达到你的目标时,则使用ref光标,正如必须用动态SQL时那样;

简言之,
首先考虑使用静态SQL,只有绝对必须使用ref光标时,才使用ref光标,

5 游标属性

%FOUND: bool - TRUE if > 1 row returned 
%NOTFOUND:bool - TRUE if 0 rows returned 
%ISOPEN: bool - TRUE if cursor still open 
%ROWCOUNTint - number of rows affected by last SQL statement 

注:NO_DATA_FOUND和%NOTFOUND的用法是有区别的,小结如下:
1)SELECT . . . INTO 语句触发 NO_DATA_FOUND
2)当一个显式cursor的 where 子句未找到时触发 %NOTFOUND
3)当UPDATE或DELETE 语句的where 子句未找到时触发 SQL%NOTFOUND
4)在cursor的Fetch循环中,要用 %NOTFOUND 或%FOUND 来确定循环的退出条件,不要用NO_DATA_FOUND。

6 sys_refcursor

sys_refcursor是oracle9i以后系统定义的一个refcursor,主要用在过程中返回结果集。

例 6

CREATE OR REPLACE PROCEDURE getEmpByDept (
	in_deptNo IN emp.deptno%TYPE, 
	out_curEmp OUT SYS_REFCURSOR
)
IS
BEGIN
	OPEN out_curEmp FOR SELECT * FROM emp WHERE deptno = in_deptNo;
    EXCEPTION
	WHEN OTHERS THEN RAISE_APPLICATION_ERROR(-20101, 'Error in getEmpByDept' || SQLCODE);
END;
/

已建立程序.

 var rset  refcursor;
 exec getEmpByDept(10,:rset);
 print rset;
  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值