PL/SQL游标(原创)

游标的相关概念及特性
定义
映射在结果集中某一行数据的具体位置,类似于C语言中的指针。即通过游标方式定位到结果集中某个特定的行,然后根据业务需求对该行进行相应特定的操作。
游标的分类
显示游标:即用户自定义游标,专门用于处理select语句返回的多行数据
隐式游标:系统自动定义的游标,记录集只有单行数据,用于处理select into 和DML语句
游标使用的一般过程
显示游标:声明, 打开, 读取, 关闭
隐式游标:直接使用读取,声明、打开、关闭都是系统自动进行的
显示游标的过程描述
a.声明游标
CURSOR cursor_name IS select_statement
如:CURSOR emp_cur IS SELECT empno,ename,job,sal FROM scott.emp;
b.打开游标
OPEN cursor_name     --打开游标则执行对应的select语句,将对应的结果集存放到游标当中
如:OPEN emp_cur
c.读取数据
FETCH cursor_name INTO var_name1,...var_name2 ;   --提取单行数据,需要配合循环语句来使用
FETCH cursor_name BULK COLLECT INTO collect1,collect2,...[LIMIT rows];    --提取多行数据,collect为集合变量
d.关闭游标
CLOSE cursor_name
5.显示游标的个属性
cursor_name%ISOPEN               游标是否打开   
cursor_name%FOUND                最近的FETCH是否提取到数据
cursor_name%NOTFOUND        最近的FETCH是否没有提取到数据
cursor_name%ROWCOUNT       返回到目前为止,已经从游标缓冲区中提取到数据的行数

显示游标应用示例

例:浏览数据,输入职位,查看每个人工资(使用fetch cursor_name into来提取单行记录)

 declare 
  v_job emp.job%type;       --定义用于存放游标提取的数据的变量
  v_name emp.ename%type;
  v_sal emp.sal%type;
  cursor emp_cur is select ename,sal from emp where job = v_job;
begin
  v_job := '&inputjob';
  dbms_output.put_line('NAME     SAL');
  open emp_cur;
  loop
    fetch emp_cur into v_name,v_sal;

     exit when emp_cur%notfound;
    dbms_output.put_line(v_name || '     ' || v_sal);
    end loop;
  close emp_cur;
end;

/

Enter value for inputjob: CLERK
old   7:   v_job := '&inputjob';
new   7:   v_job := 'CLERK';
NAME     SAL
SMITH     800
ADAMS     1100
JAMES     950
MILLER     1300
MILLER     1300

PL/SQL procedure successfully completed.

例:定义一个游标,输入部门号时,则显示该部门所有成员的名字(使用fetch cursor_name bulk collect into提取所有数据)

SQL> declare
  2    v_dept emp.deptno%type;
  3    type emp_table_type is table of varchar2(10);    --定义PL/SQL表类型
  4    emp_table emp_table_type;    --定义PL/SQL表变量存放游标数据
  5    cursor emp_cur is select ename from emp where deptno = v_dept;
  6  begin
  7     v_dept := &intputno;
  8     open emp_cur ;
  9     fetch emp_cur bulk collect into emp_table;    --使用bulk collect into提取所有数据
 10     for i in 1..emp_table.count
 11       loop
 12         dbms_output.put_line(emp_table(i));
 13       end loop;
 14     close emp_cur;
 15  end;
 16  /   
Enter value for intputno: 10
old   7:    v_dept := &intputno;
new   7:    v_dept := 10;
CLARK
KING
MILLER
PL/SQL procedure successfully completed.

例:游标属性使用示例(使用%isopen和%rowcount属性)
SQL> declare
  2    v_dept emp.deptno%type;
  3    type emp_table_type is table of varchar2(10);
  4    emp_table emp_table_type;
  5    cursor emp_cur is select ename from emp where deptno = v_dept;
  6  begin
  7    v_dept := &intputno;
  8    if not emp_cur%isopen then          --判断游标是否打开,如未打开,则打开游标
  9      open emp_cur;
 10    end if;
 11    fetch emp_cur bulk collect into emp_table; 
 12    dbms_output.put_line('The amount of record counts from cursor is ' || emp_cur%rowcount);       --使用cursor_name%rowcount 统计游标的记录数
 13    close emp_cur;
 14  end;
 15  /
Enter value for intputno: 10
old   7:   v_dept := &intputno;
new   7:   v_dept := 10;
The amount of record counts from cursor is 3

PL/SQL procedure successfully completed.

例:基于游标定义记录变量(该方式大大简化了所需要定义的变量个数)
SQL> declare
  2    cursor emp_cur is select ename,sal from emp;
  3    emp_record emp_cur%rowtype;     --定义游标类型记录变量
  4  begin
  5    open emp_cur;
  6    loop
  7      fetch emp_cur into emp_record;
  8      exit when emp_cur%notfound;   
  9      dbms_output.put_line('Employee Nmae: ' || emp_record.ename || '.The salary: ' || emp_record.sal);
 10    end loop;
 11    close emp_cur;
 12  end;
 13  /
Employee Nmae: SMITH.The salary: 800
Employee Nmae: ALLEN.The salary: 1600
...................................................................
PL/SQL procedure successfully completed.
使用游标更新记录       
通过游标既可以逐行检索结果集中的记录,又可以更新或删除当前游标行的数据。如果要通过游标更新和删除数据,在定义游标时必须要带有FOR UPDATE子句
格式: 
CURSOR cursor_name IS select_statement FOR UPDATE [ OF column_reference ][NOWAIT]
FOR UPDATE :子句用于在游标结果集数据上加行共享锁,以防止其它用户在相应行上执行DML操作
OF :子句用于游标子查询到多张表时来确定哪些表要加锁,如未指定,则select语句所引用的全部表将被加锁
NOWAIT :子句指定不等待锁
使用DML语句操作游标中的当前行时,需要在update或delete语句中引用where current of子句
UPDATE tbname set col1=.. WHERE CURRENT OF cursor_name;
DELETE tbname  WHERE CURRENT OF cursor_name;
例:使用游标修改所有记录的工资,根据JOB来作不同的修改。
SQL>   declare
  2     v_job tb_emp.job%type;
  3     cursor emp_cur is select job from tb_emp for update;
  4   begin
  5     open emp_cur;
  6     loop
  7       fetch emp_cur into v_job;
  8       exit when emp_cur%notfound ;
  9    --   exit when emp_cur%notfound ;
 10       case v_job
 11          when 'CLERK' THEN
 12            update tb_emp set sal = 1.1 * sal where current of emp_cur;
 13          when 'SALESMAN' then
 14            update tb_emp set sal = 1.2 * sal where current of emp_cur;
 15          else
 16            update tb_emp set sal = 1.3 * sal where current of emp_cur;
 17          end case;
 18      end loop;
 19      close emp_cur;
 20*  end;

/
PL/SQL procedure successfully completed.

例:利用游标删除数据

SQL> declare
  2    v_job tb_emp.job%type;
  3    v_sal tb_emp.sal%type;
  4    cursor emp_cur is select job,sal from tb_emp for update;
  5  begin
  6    open emp_cur;
  7    while emp_cur%found loop
  8      fetch emp_cur into v_job,v_sal;
  9        if v_sal > 3000 then
 10          delete tb_emp where current of emp_cur;
 11        end if;
 12    end loop;
 13    close emp_cur;
 14  end;
 15  /
PL/SQL procedure successfully completed.

例:使用OF子句对特定的表加共享锁

SQL> declare
  2    cursor emp_cur is
  3    select e.ename,e.sal,d.dname,e.deptno
  4    from tb_emp e ,dept d
  5    where e.deptno = d.deptno for update of e.deptno;
  6    emp_record emp_cur%rowtype;
  7  begin
  8    open emp_cur;
  9    loop
 10      fetch emp_cur into emp_record;

11       exit when emp_cur%notfound;
 12        if emp_record.deptno = 20 then
 13          update tb_emp set sal = 1.1 * sal where current of emp_cur;
 14        end if;
 15      dbms_output.put_line('Ename: ' || emp_record.ename || ' Sal: ' || emp_record.sal || ',Deptname: ' || emp_record.dname);
 16      end loop;
 17      close emp_cur;
 18  end;
 19  /
Ename: SMITH Sal: 800,Deptname: RESEARCH
Ename: ALLEN Sal: 1600,Deptname: SALES

..............................................

例:NOWAIT子句的使用

declare
  v_old_sal emp.sal%type;
  cursor emp_cur is select sal from tb_emp for update nowait;
begin
  open emp_cur;
  loop
    fetch emp_cur into v_old_sal;

    exit when emp_cur%notfound;
    if v_old_sal < 2000 then
      update tb_emp set sal = sal + 200 where current of emp_cur;
    end if;
  end loop;
  close emp_cur;
end;

 ERROR at line 1:
ORA-00054: resource busy and acquire with NOWAIT specified
ORA-06512: at line 5
ORA-06512: at line 7

游标FOR循环
游标FOR循环是为了简化游标使用过程而设计的。使用游标FOR循环检索游标时,游标的打开、数据提取、数据是否检索到的判断与游标的关闭都是ORACLE系统自动进行的。
游标FOR循环两种语句格式:
格式一:
先在定义部分定义游标,然后在游标FOR循环中引用该游标
    FOR record_name IN cursor_name LOOP
        statement1;
        statement2;
    END LOOP;
格式二:
在FOR循环中直接引用子查询,隐式定义游标
    FOR record_name IN subquery LOOP
        statement;

例:定义游标并使用for循环逐个显示记录

SQL> declare
  2    v_job emp.job%type;
  3    cursor emp_cur is select ename,sal from emp where job = v_job;
  4  begin
  5    v_job := '&inputjob';
  6    dbms_output.put_line('No.     Name.     Sal');
  7    for emp_record in emp_cur loop
  8       dbms_output.put_line(emp_cur%rowcount || '     ' || emp_record.ename || '     ' || emp_record.sal);
  9    end loop;
 10    end;
 11    /
Enter value for inputjob: CLERK
old   5:   v_job := '&inputjob';
new   5:   v_job := 'CLERK';
No.     Name.     Sal
1     SMITH     800
2     ADAMS     1100
3     JAMES     950
4     MILLER     1300

例:直接在游标for循环中使用子查询来逐个显示记录

SQL> declare
  2    v_job emp.job%type;
  3  begin
  4    v_job := '&intput';
  5    dbms_output.put_line('Name     Sal');
  6    for emp_record in (select ename,sal from emp where job = v_job) loop
  7      dbms_output.put_line(emp_record.ename || '     ' || emp_record.sal);
  8    end loop;
  9    end;
 10   /
Enter value for intput: CLERK
old   4:   v_job := '&intput';
new   4:   v_job := 'CLERK';
Name     Sal
SMITH     800
ADAMS     1100
JAMES     950
MILLER     1300
PL/SQL procedure successfully completed.

参数游标
参数游标是指带有参数的游标,与存储过程和函数相似,可以将参数传递给游标并在查询中使用。当定义了参数游标后,使用不同的参数值多次打开游标则会生成不同的结果集。这对于处理在某种条件下打开游标的情况非常有用。定义参数游标:

cursor cursor_name[(parameter[,parameter],...)] is select_statement;
定义参数的语法如下:
parameter_name [in] data_type[{:=|default} value]

注:datatype 只指定数据类型即可,不能指定参数的长度、精度、刻度
打开参数游标:
OPEN cursor_name [(vlaues)]
参数个数、类型必须与定义时的形参相匹配。对于定义的参数游标,一定要在游标子查询的where子句中指定定义的参数,否则将使得参数游标失去意义

SQL> declare
  2    cursor c_dept is select * from dept;
  3    cursor c_emp(p_dept varchar2) is select ename,sal from emp where deptno = p_dept;
  4    r_dept dept%rowtype;
  5    v_ename emp.ename%type;
  6    v_sal emp.sal%type;
  7    v_tot_sal emp.sal%type;
begin
  open c_dept;
    fetch c_dept into r_dept;
  loop
    fetch c_dept into r_dept;

    exit when c_dept%notfound;

    open c_emp(r_dept.deptno);
    dbms_output.put_line('Department:' || r_dept.dname);
      exit when c_emp%notfound;
    v_tot_sal := 0;
    open c_emp(r_dept.deptno);
    loop
      
 18        fetch c_emp into v_ename,v_sal;

             exit when c_emp%notfound;

19        dbms_output.put_line('Name:' || v_ename || ',Sal:' || v_sal);
      v_tot_sal := v_tot_sal + v_sal;
      end loop;
 22        close c_emp;
 23      dbms_output.put_line('Total sal for dept:' || v_tot_sal);
 24      end loop;
 25      close c_dept;
 26  end;
 27  /
Department:ACCOUNTING
Name:CLARK,Sal:2450
Name:KING,Sal:5000
Name:MILLER,Sal:1300
Name:MILLER,Sal:1300
Total sal for dept:10050
Department:RESEARCH

......................................
隐式游标的定义及其属性
定义
隐式游标则由则由系统自动定义,非显示定义游标的DML语句即被赋予隐式游标属性。其过程由oracle控制,完全自动化。隐式游标的名称是SQL,不能对 SQL游标显式地执行OPEN,FETCH,CLOSE语句。当系统使用一个隐式游标时,可以通过隐式游标的属性来了解操作的状态和结果,进而控制程序的 流程。隐式游标可以使用名字SQL来访问,但要注意,通过SQL游标名总是只能访问前一个处理操作或单行SELECT操作的游标属性。所以通常在刚刚执行完操作之后,立即使用SQL游标名来访问属性。
属性
类似于显示游标,隐式游标同样具有四种属性,只不过隐式游标以SQL%开头,而显示游标以Cursor_name%开头。通过SQL%总是只能访问前一个DML操作或单行SELECT操作的游标属性,用于判断DML执行的状态和结果,进而控制程序的流程
SQL%ISOPEN:游标是否打开。当执行select into ,insert update,delete时,Oracle会隐含地打开游标,且在该语句执行完毕或隐含地关闭游标,因为是隐式游标,故SQL%ISOPEN总是false  
SQL%FOUND:判断SQL语句是否成功执行。当有作用行时则成功执行为true,否则为false,未执行sql时为NULL。
SQL%NOTFOUND:判断SQL语句是否成功执行。当有作用行时否其值为false,否则其值为true,未执行sql时为NULL。
SQL%ROWCOUNT:在执行任何DML语句之前,SQL%ROWCOUNT的值都是NULL。执行了DML或者SELECT语句后,SQL%ROWCOUNT的值将为该语句作用的行数。

Notice: SQL%ROWCOUNT与cursor_name%rowcount是有所不同的,sql%rowcount返回的是上一次sql所处理的行数,cursor_name%rowcount返回的是,当前cursor所处理的行数

对于SELECT INTO语句处理的结果包括三种种情况

a.查询结果返回单行,SELECT INTO被成功执行
b.查询结果没有返回行,PL/SQL将抛出no_data_found异常
c.查询结果返回多行,PL/SQL将抛出too_many_rows 异常
对于上述两种异常发生时,类似于普通异常处理,程序控制权转移到异常处理部分(如没有异常处理则程序中断)。对于异常被激后发,SQL游标的四个属性在此将不可使用,如下面的例子。

SQL> declare
  2    v_dept emp.deptno%type := &no;
  3  begin
  4    if sql%rowcount >= 0 then    --判断执行前的SQL%ROWCOUNT属性
  5      dbms_output.put_line('SQL%ROWCOUNT value is ' || SQL%ROWCOUNT || 'before updated');
  6    else
  7      dbms_output.put_line('SQL%ROWCOUNT value is NULL before updated');
  8    end if;
  9    update emp set sal = sal + 300 where deptno = v_dept;
 10    if sql%found then     --判断SQL%FOUND的属性
 11      dbms_output.put_line('SQL executes successfully');
 12      dbms_output.put_line('SQL%FOUND is True');
 13    end if;
 14    if sql%notfound then   --判断SQL%NOTFOUND的属性
 15      dbms_output.put_line('SQL%NOUTFOUND is True');
 16    else
  end if;dbms_output.put_line('SQL%NOTFOUND is false');
    dbms_output.put_line('SQL%NOTFOUND is false');
  end if;
  if sql%isopen then     --判断SQL%ISOPEN属性
    dbms_output.put_line('SQL%ISOPEN is True');
  else
    dbms_output.put_line('SQL%ISOPEN is False');
  end if;
  dbms_output.put_line('The row updated is :' || SQL%ROWCOUNT);    --判断执行后SQL%ROWCOUNT的属性
 25  end;
 26  /

--下面是成功更新后的结果
Enter value for no: 10
old   2:   v_dept emp.deptno%type := &no;
new   2:   v_dept emp.deptno%type := 10;
SQL%ROWCOUNT value is NULL before updated
SQL executes successfully
SQL%FOUND is True
SQL%NOTFOUND is false
SQL%ISOPEN is False
The row updated is :3
PL/SQL procedure successfully completed.

--下面是更新失败后的结果
SQL> /
Enter value for no: 80
old   2:   v_dept emp.deptno%type := &no;
new   2:   v_dept emp.deptno%type := 80;
SQL%ROWCOUNT value is NULL before updated
SQL%NOUTFOUND is True
SQL%ISOPEN is False
The row updated is :0
PL/SQL procedure successfully completed.

SQL%BULK_ROWCOUNT:Composite attribute that is like an associative array whose ith element is the number of rows affected by the ith DML statement in the most recently completed FORALL statement. Restriction on SQL%BULK_ROWCOUNT ,You cannot assign the value of SQL%BULK_ROWCOUNT(index) to another collection.

Note:uses SQL%BULK_ROWCOUNT to show how many rows each DELETE statement in the FORALL  statement deleted and SQL%ROWCOUNT to show the total number of rows deleted.
SQL%BULK_EXCEPTIONS
Composite attribute that is like an associative array of information about the DML statements that failed during the most recently run FORALL statement. SQL%BULK_EXCEPTIONS.COUNT is the number of DML statements that failed. If SQL%BULK_EXCEPTIONS.COUNT is not zero, then for each index value i from 1 through SQL%BULK_EXCEPTIONS.COUNT:SQL%BULK_EXCEPTIONS(i).ERROR_INDEX is the number of the DML statement that failed.SQL%BULK_EXCEPTIONS(i).ERROR_CODE is the Oracle Database error code for the failure.
For example, if a FORALL SAVE EXCEPTIONS statement runs 100 DML statements, and the tenth and sixty-fourth ones fail with error codes ORA-12899 and ORA-19278, respectively, then:
SQL%BULK_EXCEPTIONS.COUNT = 2
SQL%BULK_EXCEPTIONS(1).ERROR_INDEX = 10
SQL%BULK_EXCEPTIONS(1).ERROR_CODE = 12899
SQL%BULK_EXCEPTIONS(2).ERROR_INDEX = 64
SQL%BULK_EXCEPTIONS(2).ERROR_CODE = 19278

关于这两个游标的使用例子请参见笔者的这篇文章

http://czmmiao.iteye.com/blog/1825825

 

参考至:http://blog.csdn.net/robinson_0612/article/details/6078622
               http://blog.csdn.net/robinson_0612/article/details/6092066

               http://hi.baidu.com/graceyan/item/a750fe000c2a30ea349902aa

               http://ftc007.blog.163.com/blog/static/1965716502012315104852764/

               http://docs.oracle.com/cd/E11882_01/appdev.112/e25519/tuning.htm#i49099

               http://docs.oracle.com/cd/E11882_01/appdev.112/e25519/sql_cursor.htm#i36237

本文原创,转载请注明出处、作者

如有错误,欢迎指正

邮箱:czmcj@163.com

作者:czmmiao  文章出处:http://czmmiao.iteye.com/blog/1816935

时间: 2024-09-01 10:36:18

PL/SQL游标(原创)的相关文章

PL/SQL 游标变量

    游标变量与游标相似,有其共性,也有其不同点.就其共性来说两者都是指向多行查询的结果集中的当前行.都要经历声明,打开,检索与关闭的过程.所不同的是游标与游标变量类似于常量与变量.游标是静态的,而游标变量是动态的,因为游标变量并不与某个特定的查询相绑定.所以,游标变量可以打开任何类型兼容的查询.其次可以将游标变量作为参数传递给本地和存储子程序.本文主要描述游标变量的使用.   一.什么是游标变量    显示游标用于命名一个工作区域,其中保存多行查询的信息,而且该游标始终指向工作区域的内容.而

PL/SQL异常处理(原创)

Exception概述 Exception是一种PL/SQL标识符,当运行的PL/SQL块出现错误或警告,则会触发异常处理.为了提高程序的健壮性,可以在PL/SQL块中引入异常处理部分,进行捕捉异常,并根据异常出现的情况进行相应的处理.ORACLE异常分为两种类型:系统异常.自定义异常.其中系统异常又分为:预定义异常和非预定义异常.预定义异常ORACLE定义了他们的错误编号和异常名字,常见的预定义异常处理如下 错误号 异常错误信息名称 说明 ORA-0001 Dup_val_on_index 违

Oracle数据库之PL/SQL游标学习笔记

1. 游标概念 字面意思是游动的光标,是指向上下文区域的句柄或指针. 在PL/SQL块中执行CRUD操作时,ORACLE会在内存中为其分配上下文区.用数据库语言来描述游标就是:映射在上下文区结果集中一行数据上的位置实体. 用户可以使用游标访问结果集中的任意一行数据,将游标指向某行后,即可对该行数据进行操作.游标为应用提供了一种对具有多行数据查询结果集中的每一行数据分别进行单独处理的方法,是设计嵌入式SQL语句的应用程序的常用编程方式. 在每个用户会话中,可以同时打开多个游标,其最大数量由数据库初

PL/SQL --&amp;gt;隐式游标(SQL%FOUND)

--=============================== -- PL/SQL -->隐式游标(SQL%FOUND) --===============================       在PL/SQL中,游标的使用分为两种,一种是显示游标,一种是隐式游标,显示游标的使用需要事先使用declare来进行声明,其过程包括 声明游标,打开游标,从游标提取数据,关闭游标.该方式多用于处理select语句返回的多行数据的情形.而隐式游标则由则由系统自动定义 ,当DML被使用时,Orac

PL/SQL --&amp;gt; 游标

--================== -- PL/SQL --> 游标 --==================   一.游标的相关概念及特性     1.定义         映射在结果集中某一行数据的具体位置,类似于C语言中的指针.即通过游标方式定位到结果集中某个特定的行,然后根据业务需求         对该行进行相应特定的操作.             2.游标的分类         显示游标:即用户自定义游标,专门用于处理select语句返回的多行数据         隐式游标:系

PL/SQL下SQL结果集以html形式发送邮件

       在运维的过程中,有时候需要定时将SQL查询的数据结果集以html表格形式发送邮件,因此需要将SQL查询得到的结果集拼接成html代码.对于这种情形通常有二种方式来完成.一是直接使用cron job来定时轮询并借助os级别的邮件程序来完成.其查询结果集可以直接在SQL*Plus下通过设置html标签自动实现html表格形式.一种方式是在Oracle中使用scheduler job来定时轮询.这种方式需要我们手动拼接html代码.本文即是对第二种情形展开描述. 关于PL/SQL下如何发

PL/SQL --&amp;gt; DML 触发器

--======================= -- PL/SQL --> DML 触发器 --=======================         何谓触发器?简言之,是一段命名的PL/SQL代码块,只不过该代码块在特定的条件下被触发并且执行.对于这样的代码我们称之为触发器 .触发器根据触发类型的不同又分为不同级别的触发器,下面将给出触发器的分类,定义,以及使用的示例.   一.触发器的相关概念     1.触发器的分类         通常根据触发条件以及触发级别的不同分为DM

PL/SQL --&amp;gt; INSTEAD OF 触发器

--============================== -- PL/SQL --> INSTEAD OF 触发器 --==============================       INSTEAD OF 触发器常用于管理编写不可更新的视图,INSTEAD-OF触发器必须是行级的.     可以用INSTEAD OF触发器来解释INSERT.UPDATE和DELETE语句,并用备用的程序代码替换那些指令.   一.不可更新视图     基于下列情形创建的视图,不可直接对其进行D

PL/SQL --&amp;gt; 函数

--================== -- PL/SQL --> 函数 --==================       函数通常用于返回特定的数据.其实质是一个有名字的PL/SQL块,作为一个schema对象存储于数据库,可以被反复执行.函数通常被作为 一个表达式来调用或存储过程的一个参数,具有返回值.   一.建立函数的语法     CREATE [ OR REPLACE ] FUNCTION function_name         (argument1 [mode1] data