显式是相对与隐式cursor而言的,就是有一个明确的声明的cursor。
显式游标的声明类似如下 :
cursor cursor_name (parameter list) is select ...
游标一个完整的生命周期:
declare->open->fetch->close
下面看一个简单的静态显式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,
然后,根据不同的号段输出当前系统中该号短对应的可用手机号码。
隐式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;
/
ref cursor属于动态cursor(直到运行时才知道这条查询)。
从技术上讲,在最基本的层次,静态cursor和ref cursor是相同的。
选择哪种方法,由逻辑确定(一个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;
/
(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
ref cursor 使用仅限于以下几种情况:
简言之,
首先考虑使用静态SQL,只有绝对必须使用ref光标时,才使用ref光标,
%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)当一个显式cursor的 where 子句未找到时触发 %NOTFOUND;
3)当UPDATE或DELETE 语句的where 子句未找到时触发 SQL%NOTFOUND;
4)在cursor的Fetch循环中,要用 %NOTFOUND 或%FOUND 来确定循环的退出条件,不要用NO_DATA_FOUND。
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;