文章目录
游标
- 游标是一个指针,指向一块SQL中用来存储处理过来的增删改查操作返回的数据
- 会话游标会在会话结束前保持活跃
- 隐式游标是PL/SQL创建并管理
- 显示游标由用户创建并管理
show v$open_cursor
查看当前会话游标- 详情参考oracle中cursor与refcursor及sys_refcursor的区别
1.1 显式游标
1.1.1 创建游标
- 语法:
CURSOR cursor_name IS sql_statements;
- 举例:
CURSOR selectemp IS select ename,job,sal from scott.emp;
1.1.2 打开游标
- 语法:
OPEN cursor_name [argument [,argument·····]];
即执行创建游标时关联的SQL语句
1.1.3 获取数据
- 语法:
FETCH cursor_name INTO variable [,variable];
获取记录集合中一行数据放入变量中
1.1.4 关闭游标
- 语法:
CLOSE cursor_name
游标不能重复打开,必须在打开前将其关闭
1.1.5 使用实例
#声明变量
create or replace procedure cursortest
is #创建一个过程
#三个变量分别于用户scott中EMP表的三个列(ENAME/JOB/SAL)对应
employeename varchar2(20);
employeejob varchar2(9);
employeesal number(7,2);
#声明游标
cursor cselectemp #定义游标并且赋值(is 不能和cursor分开使用)
is
select ename,job,sal
from scott.emp;#与游标关联的SQL语句
begin
#打开游标
open cselectemp;
#获取数据
LOOP
FETCH cselectemp
INTO employeename,employeejob,employeesal;
EXIT WHEN cselectemp%not found;#循环结束的标志是游标的属性值%NOTFOUND为真,即已遍历完所有的记录
DBMS_OUTPUT.put_line('employeename is '
||employeename
||', employeejob is '
||employeejob
||', employeesal is'
||employeesal );
END LOOOP;
close cselectemp;#游标占用内存,必须关闭游标释放内存资源
end;
执行结果
1.2 隐式游标
- 增删改查操作时PL/SQL会自动创建一个隐式游标
- 无法控制,涉及SQL语句执行结束时会会自动关闭
EX:
使用隐式游标记录表EMP中不同岗位的数量并打印输出: 以下包含FOR游标示例
SQL> create
2 or replace procedure hidencursortest is
3 jobnumber NUMBER;
4 cursor cselectemp
5 is
6 select
7 ename,
8 job,
9 sal
10 from
11 scott.emp;
12
13 begin
14 select count(distinct(job))
15 into jobnumber
16 from emp;#查询EMP表中不同工作岗位的数量
#此时SQL语句为一个隐性游标
17 DBMS_OUTPUT.put_line(
18 'there are ' || jobnumber || 'different jobs' );
#输出隐式游标执行的SQL语句返回的记录数
19 DBMS_OUTPUT.put_line(
20 ' hiden cursor rowcount is '||SQL%ROWCOUNT)
21 for emp_record in cselectemp
22 loop
23 DBMS_OUTPUT.put_line(
24 'employeename is ' || emp_record.ename || ', employeejob is ' || emp_record.job || ', employeesal is' || emp_record.sal
25 );
26 end loop;
27
28 end;
29 /
过程已创建。
#执行过程
SQL> execute hidencursortest
there are 5different jobs
hiden cursor rowcount is 1
employeename is SMITH, employeejob is CLERK, employeesal is800
employeename is ALLEN, employeejob is SALESMAN, employeesal is1600
employeename is WARD, employeejob is SALESMAN, employeesal is1250
····省略····
employeename is FORD, employeejob is ANALYST, employeesal is3000
employeename is MILLER, employeejob is CLERK, employeesal is1300
PL/SQL 过程已成功完成。
1.2.2 游标属性
-
SQL%ISOPEN:判断游标是是否打开
一般是FALSE -
SQL%FOUND:游标发现数据
· NULL:没有增删改查语句运行
· TRUE:有运行
· FALSE:其他情况
· EX:SQL> create or replace procedure ptestfound ( 2 dept_no NUMBER 3 ) AUTHID DEFINER AS 4 BEGIN 5 DELETE FROM dept 6 WHERE deptno = dept_no;#PL/SQL中执行DML操作自动创建维护一个隐性游标 #使用SQL%FOUND获取游标属性 7 IF SQL%FOUND THEN #如果DML语句执行成功,则其值为TRUE 8 DBMS_OUTPUT.PUT_LINE ( 9 'Delete succeeded for dept numb' || dept_no); 10 ELSE #如果DML语句执行失败,则其值为FALSE 11 DBMS_OUTPUT.PUT_LINE ( 12 'No department number' || dept_no); 13 END IF; 14 END; 15 / 过程已创建。 SQL> begin 2 ptestfound(260); 3 ptestfound(10); 4 end; 5 / No department number260 Delete succeeded for dept numb10 PL/SQL 过程已成功完成。
执行后如果有报错
违反完整约束条件 (SCOTT.FK_DEPTNO) - 已找到子记录
,参考《ORA-02292: 违反完整约束条件》 -
SQL%NOTFOUND:游标没有发现数据(与SQL%FOUND相反)
· NULL:没有增删改查语句运行
· FALSE:有运行
· TRUE:其他情况 -
SQL%ROWCOUNT:游标设计的返回结果的行数量
SQL> list 1 declare 2 emp_no number :=&empno; 3 begin 4 delete from emp_test where empno = emp_no; 5 dbms_output.put_line('emp deleted is : ' ||to_char(SQL%ROWCOUNT)); 6* end; SQL> / 输入 empno 的值: 7369 原值 2: emp_no number :=&empno; 新值 2: emp_no number :=7369; emp deleted is : 1 #表示删除成功一行 输入 empno 的值: 555 原值 2: emp_no number :=&empno; 新值 2: emp_no number :=555; emp deleted is : 0#表示未有删除行的操作 PL/SQL 过程已成功完成。 ```
1.3 FOR游标
语法格式
FOR record IN cursor_name
LOOP
Logical statements.....
END LOOP;
- record为一条记录集合,自动定义为%ROWTYPE:包含记录中多列变量类型
- LOOP循环时打开游标,结束时自动关闭
EX:
点击跳转到FOR游标示例
1.4 游标变量
第一个示例直接上代码吧
注意:
系统定义的sys_refcursor
游标赋值用的是for,cursor
用的是is
#创建函数cursoremp 用于返回一个游标
create or replace function cursoremp return sys_refcusor
is
empcursor sys_refcursor;#系统定义的ref游标,在过程中返回结果集
begin
#游标指向SELECT语句返回的数据区
open empcursor for select * from emp where sal > 1000;
#此处sys_refcusor赋值用的是for,之前cursor用的是is
return empcursor;
end;
#创建一个过程用于调用游标给其他游标变量赋值
create or replace procedure print_emp
is
var_cursor sys_refcursor;
var_row emp % rowtype;
begin
#将函数cursoremp返回的游标赋值给游标变量var_cursor
var_cursor := cursoremp;
loop
#将游标变量var_cursor获取的结果集放入变量var_row中
fetch var_cursor into var_row;
if var_cursor % notfound then
exit;
end if;
dbms_output.put_line(var_row.ename || ' : ' || var_row.sal);
end loop;
close var_cursor;#记得关闭游标释放内存
end;
#执行print_emp过程
SQL> exec print_emp;
ALLEN : 1600
WARD : 1250
····省略····
SCOTT : 3000
KING : 5000
第二个示例:
在客户端定义一个游标变量var_sqlcur,再调用服务端的定义的存储过程和函数,建立两者之间连接
SQL> variable var_sqlcur refcursor; #定义一个游标变量var_sqlcur
SQL> list
1 declare
2 var_row emp%rowtype;
3 begin
4 :var_sqlcur := cursoremp;#将函数cursoremp返回的游标赋值给游标变量var_sqlcur “:var_sqlcur”不可去掉冒号,将报错
5 loop
6 fetch :var_sqlcur into var_row;
7 exit when :var_sqlcur%notfound;
8 dbms_output.put_line(var_row.ename||' : '||var_row.sal);
9 end loop;
10 close :var_sqlcur;
11* end;
SQL> /
ALLEN : 1600
WARD : 1250
····省略····
FORD : 3000
MILLER : 1300
第三个示例:
直接在客户端调用服务端函数cursoremp,然后将获得的游标值赋值给客户端的游标变量var_sqlcur
SQL> exec :var_sqlcur :=cursoremp;
#打印游标获取的数据
SQL> print var_sqlcur;
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- -------------- ---------- ---------- ----------
7499 ALLEN SALESMAN 7698 20-2月 -81 1600 300 30
7521 WARD SALESMAN 7698 22-2月 -81 1250 500 30
7566 JONES MANAGER 7839 02-4月 -81 2975 20
···以下省略····
1.4 游标表达式
使用游标表达式来实现数据指针的作用,指向SQL语句 返回数据的存储地址
SQL> list
1 select deptno,dname,loc,
#定义一个游标表达式
2 cursor (select empno,ename,sal
3 from emp
4 where deptno=d.deptno)
5* from dept as d;
SQL> /
DEPTNO DNAME LOC CURSOR(SELECTEMPNO,E
---------- -------------- ------------- --------------------
10 ACCOUNTING NEW YORK CURSOR STATEMENT : 4
#从DEPT表中读取到DEPTNO=10的数据时,随着游标打开将从EMP表中获得DEPTNO为10的所有记录
CURSOR STATEMENT : 4 #4代表 DEPTNO,DNAME,LOC,CURSOR(···)四个字段
EMPNO ENAME SAL
---------- ---------- ----------
7782 CLARK 2450
7839 KING 5000
7934 MILLER 1300
#返回DEPTNO=20的值
20 RESEARCH DALLAS CURSOR STATEMENT : 4
CURSOR STATEMENT : 4
EMPNO ENAME SAL
---------- ---------- ----------
7369 SMITH 800
7566 JONES 2975
7788 SCOTT 3000
7876 ADAMS 1100
7902 FORD 3000
#返回DEPTNO=30的值
30 SALES CHICAGO CURSOR STATEMENT : 4
CURSOR STATEMENT : 4
EMPNO ENAME SAL
---------- ---------- ----------
7499 ALLEN 1600
7521 WARD 1250
7654 MARTIN 1250
7698 BLAKE 2850
7844 TURNER 1500
7900 JAMES 950
已选择6行。
40 OPERATIONS BOSTON CURSOR STATEMENT : 4
CURSOR STATEMENT : 4
未选定行
使用嵌套游标 以下包含动态游标示例
SQL> declare
2 type var_cur_type is ref cursor; #声明REF游标为一个TYPE
3 var_cur var_cur_type;#创建该类型的一个实例
4 dept_name dept.dname%type;
5 emp_name emp.ename%type;
6 #定义显示游标cur
#主游标的查询用于获得dname和游标表达式获得ename
7 cursor cur is
8 select dname,
9 cursor(select e.ename
10 from emp e
11 where e.deptno=d.deptno) emp
12 from dept d
13 where dname like 'R%';
14 begin
15 open cur;
16 loop
17 fetch cur into dept_name,var_cur;
18 exit when cur%notfound;
19 dbms_output.put_line('dept_name : '|| dept_name);
20 loop
#var_cur是游标,需要传值给变量emp_name
21 fetch var_cur into emp_name;
22 exit when var_cur%notfound;
23 dbms_output.put_line('emp_name: '||emp_name);
24 end loop;
25 end loop;
26 close cur;#对应open cur;
27 end;
28 /
dept_name : RESEARCH #dept_name为RESEARCH ,对应DEPT表中的部门号是20
#所以在游标表达式中获得部门号为20的所有dname的值
emp_name: SMITH
emp_name: JONES
emp_name: SCOTT
emp_name: ADAMS
emp_name: FORD
#验证是否一致
SQL> select ename from emp where
2 deptno in (select deptno
3 from dept
4 where dname='RESEARCH');
ENAME
----------
SMITH
JONES
SCOTT
ADAMS
FORD
1.5 动态游标
- 即REF游标,是一个记录的集合
- 使用前需先声明它为一个TYPE
TYPE ref 游标名 IS REF CURSOR [返回类型] ex: TYPE ty_emprefcur IS REF CURSOR
- 使用时需创建该类型的实例
点击跳转到动态游标示例empcursor ty_emprefcur;
来源:CSDN
作者:w.ang.jie
链接:https://blog.csdn.net/qq_32392597/article/details/104750563