PL_SQL模块学习之十、游标

拈花ヽ惹草 提交于 2020-03-11 01:06:27

游标

  • 游标是一个指针,指向一块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;
    
    点击跳转到动态游标示例
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!