Oracle数据库游标

Oracle数据库游标使用方法

2009-12-30 15:27

SQL是用于访问Oracle数据库的语言,PL/SQL扩展和加强了SQL的功能,它同时引入了更强的程序逻辑下面在本文中将对游标的使用进行一下讲解,希望可以和大家共同学习进步。 

  游标字面理解就是游动的光标。游标是SQL的一个内存工作区,由系统或用户以变量的形式定义。在某些情况下,需要把数据从存放在磁盘的表中调到计算机内存中进行处理,最后将处理结果显示出来或最终写回数据库。这样数据处理的速度才会提高,否则频繁的磁盘数据交换会降低效率。用数据库语言来描述游标就是映射在结果集中一行数据上的位置实体,有了游标,用户就可以访问结果集中的任意一行数据了,将游标放置到某行后,即可对该行数据进行操作,例如提取当前行的数据等。

  游标有两种类型:显式游标和隐式游标。游标一旦打开,数据就从数据库中传送到游标变量中,然后应用程序再从游标变量中分解出需要的数据,并进行处理。

  当系统使用一个隐式游标时,可以通过隐式游标的属性来了解操作的状态和结果,进而控制程序的流程。隐式游标可以使用名字SQL来访问,但要注意,通过SQL游标名总是只能访问前一个处理操作或单行SELECT操作的游标属性。所以通常在刚刚执行完操作之后,立即使用SQL游标名来访问属性。游标的属性有四种,分别是SQL %ISOPENSQL %FOUNDSQL %NOTFOUNDSQL %ROWCOUNT

  SQL%ISOPEN返回的类型为布尔型,判断游标是否被打开,如果打开%ISOPEN等于true,否则等于false,即执行过程中为真,结束后为假。

  SQL%NOTFOUND返回值为布尔型,判断游标所在的行是否有效,如果有效,则%FOUNDD等于true,否则等于false,即与%FOUND属性返回值相反。

  SQL%FOUND返回值的类型为布尔型,值为TRUE代表插入 删除 更新或单行查询操作成功。

  SQL%ROWCOUNT返回值类型为整型,返回当前位置为止游标读取的记录行数,即成功执行的数据行数。 示例代码如下:

      set serveroutput on;
  declare
  varno varchar2(20);
  varprice varchar2(20);
  CURSOR mycur(vartype number) is
  select emp_no,emp_zc from cus_emp_basic
  where com_no = vartype;
  begin
  if mycur%isopen = false then
  open mycur(043925);
  end if;
  fetch mycur into varno,varprice;
  while mycur%found
  loop
  dbms_output.put_line(varno||','||varprice);
  if mycur%rowcount=2 then
  exit;
  end if;
  fetch mycur into varno,varprice;
  end loop;
  close mycur;
  end;



  记录并不保存在数据库中,它与变量一样,保存在内存空间中,在使用记录时候,要首先定义记录结构,然后声明记录变量。可以把PL/SQL记录看作是一个用户自定义的数据类型。

  游标for循环是显示游标的一种快捷使用方式,它使用for循环依次读取结果集中的行数据,当form循环开始时,游标自动打开(不需要open),每循环一次系统自动读取游标当前行的数据(不需要fetch),当退出for循环时,游标被自动关闭(不需要使用close)。使用游标for循环的时候不能使用open语句,fetch语句和close语句,否则会产生错误。

   set serveroutput on;
  declare
  cursor mycur(vartype number)is
  select emp_no,emp_zc from cus_emp_basic
  where com_no=vartype;
  begin
  for person in mycur(000627) loop
  dbms_output.put_line('编号:'||person.emp_no||',住址:'||person.emp_zc);
  end loop;
  end; 

  显式游标的使用总共分4个步骤:

  1.声明游标

  在DECLEAR部分按以下格式声明游标:

  CURSOR 游标名[(参数数据类型[参数数据类型...])]

  IS SELECT语句;

  参数是可选部分,所定义的参数可以出现在SELECT语句的WHERE子句中。如果定义了参数,则必须在打开游标时传递相应的实际参数。

  SELECT语句是对表或视图的查询语句,甚至也可以是联合查询。可以带WHERE条件、ORDER BYGROUP BY等子句,但不能使用INTO子句。在SELECT语句中可以使用在定义游标之前定义的变量。

  例:

    DELCARE
  CURSOR C_EMP IS SELECT empno,ename,salary
  FROM emp   WHERE salary>1500
  ORDER BY ename;
  ........
  BEGIN



  在游标定义中SELECT语句中不一定非要表可以是视图,也可以从多个表或视图中选择.

  2.打开游标

  使用游标中的值之前应该首先打开游标,打开游标初始化查询处理。打开游标的语法

  OPEN 游标名[(实际参数1[实际参数2...])];

  打开游标时,SELECT语句的查询结果就被传送到了游标工作区。

  例:

  OPEN C_EMP;

  3.提取数据

  从游标得到一行数据使用FETCH命令。每一次提取数据后,游标都指向结果集的下一

  FETCH cursor_name INTO variable[,variable,...]

  对于SELECT定义的游标的每一列,FETCH变量列表都应该有一个变量与之相对应,变

  在可执行部分,按以下格式将游标工作区中的数据取到变量中。提取操作必须在打开游标之后进行。

  FETCH 游标名 INTO 变量名1[变量名2...];

  或

  FETCH 游标名 INTO 记录变量;

  游标打开后有一个指针指向数据区,FETCH语句一次返回指针所指的一行数据,要返回多行需重复执行,可以使用循环语句来实现。控制循环可以通过判断游标的属性来进行。

  下面对这两种格式进行说明:

  第一种格式中的变量名是用来从游标中接收数据的变量,需要事先定义。变量的个数和类型应与SELECT语句中的字段变量的个数和类型一致。

  第二种格式一次将一行数据取到记录变量中,需要使用%ROWTYPE事先定义记录变量,这种形式使用起来比较方便,不必分别定义和使用多个变量。

  定义记录变量的方法如下:

  变量名 表名|游标名%ROWTYPE;

  其中的表必须存在,游标名也必须先定义。

4.关闭游标 

  CLOSE 游标名;

  例:

  CLOSE C_EMP;

  显式游标打开后,必须显式地关闭。游标一旦关闭,游标占用的资源就被释放,游标变成无效,必须重新打开才能使用。

  Oracle支持动态SELECT语句和动态游标,动态的方法大大扩展了程序设计的能力。

  对于查询结果为一行的SELECT语句,可以用动态生成查询语句字符串的方法,在程序执行阶段临时地生成并执行,语法是:

  execute immediate 查询语句字符串 into 变量1[,变量2...];

  在变量声明部分定义的游标是静态的,不能在程序运行过程中修改。虽然可以通过参数传递来取得不同的数据,但还是有很大的局限性。通过采用动态游标,可以在程序运行阶段随时生成一个查询语句作为游标。要使用动态游标需要先定义一个游标类型,然后声明一个游标变量,游标对应的查询语句可以在程序的执行过程中动态地说明。

oracle存储过程,游标及循环应用实例

2009-10-21 08:46

最近在项目中做了一个关于系统与系统之间数据同步的业务,之前使用jndi远程调用的方式实现了这个同步功能,但总是感觉速度很慢,而且很不稳定,现将其修改成dblink+存储过程方式,特对存储过程及游标做了相应学习,此处主要记录游标及循环的不同使用方法:

 

第一种方式: FOR ... IN .....LOOP....END LOOP

CREATE OR REPLACE PROCEDURE synchronous_material
IS  
last_version VARCHAR2(14);  --------此处注意,别忘了长度的定义
sync_row GZB_EPMS_material%ROWTYPE;    ---用这种方式,此处的sync_row也可不定义  BEGIN
SELECT last_SYNCHRONOUS_TIME INTO last_version FROM (SELECT (to_char(SYNCHRONOUS_TIME,'yyyymmddhhmiss')) AS last_SYNCHRONOUS_TIME FROM ... ORDER BY SYNCHRONOUS_TIME DESC) WHERE ROWNUM <= 1;

-----此处本来应该加EXCEPTION WHEN DATA_NOT_FOUND THEN ...;但我的这个业务不会出现找不到数据,所以省略了

IN (     ----此处的sync_row迭代变量可以不用事先在IS中定义       SELECT ......                                            
FROM ... WHERE OBJVERSION > last_version AND  OBJVERSION <= to_char(SYSDATE,'yyyymmddhhmiss')
UNION
SELECT .....     FROM ....WHERE OBJVERSION > last_version AND  OBJVERSION <= to_char(SYSDATE,'yyyymmddhhmiss')
) LOOP

NULL;  ---此处是语句体,省略;

END LOOP;     RETURN;   
END synchronous_material;

 

第二种方式:CURSOR

    1,CURSOR + WHILE...LOOP...END LOOP

 

         CREATE OR REPLACE PROCEDURE synchronous_material
IS  


last_version VARCHAR2(14); 

         CURSOR cur_sync(begin_objversion VARCHAR2,end_objversion VARCHAR2)
IS
SELECT  .....  FROM ......WHERE OBJVERSION > begin_objversion AND   

            OBJVERSION <= end_objversion
UNION
SELECT ....FROM ......WHERE OBJVERSION > begin_objversion AND 

             OBJVERSION <=   end_objversion;    

        BEGIN
SELECT last_SYNCHRONOUS_TIME INTO last_version FROM

              (SELECT (to_char (SYNCHRONOUS_TIME,'yyyymmddhhmiss'))

              AS last_SYNCHRONOUS_TIME FROM ...... ORDER BY SYNCHRONOUS_TIME DESC)

              WHERE ROWNUM <= 1;


OPEN  cur_sync(last_version,to_char(SYSDATE,'yyyymmddhhmiss'));              WHILE cur_sync%FOUND LOOP
NULL;
END LOOP;    
LOSE cur_sync; END synchronous_material;

 

2,CURSOR +FOR 变量名 IN 游标名 LOOP....END LOOP

 

CREATE OR REPLACE PROCEDURE synchronous_material
IS  


last_version VARCHAR2(14); 

         CURSOR cur_sync(begin_objversion VARCHAR2,end_objversion VARCHAR2)
IS
SELECT  .....  FROM ......WHERE OBJVERSION > begin_objversion AND   

            OBJVERSION <= end_objversion
UNION
SELECT ....FROM ......WHERE OBJVERSION > begin_objversion AND 

             OBJVERSION <=   end_objversion;       

sync_row GZB_EPMS_material%ROWTYPE;
BEGIN
SELECT last_SYNCHRONOUS_TIME INTO last_version FROM

              (SELECT (to_char (SYNCHRONOUS_TIME,'yyyymmddhhmiss'))

              AS last_SYNCHRONOUS_TIME FROM ...... ORDER BY SYNCHRONOUS_TIME DESC)

              WHERE ROWNUM <= 1;

 

     OPEN  cur_sync(last_version,to_char(SYSDATE,'yyyymmddhhmiss'));  

FOR sync_row IN cur_sync(last_version,to_char(SYSDATE,'yyyymmddhhmiss')) LOOP      NULL;
END LOOP;
CLOSE cur_sync; END synchronous_pm_t_material;

ORACLE 游标总结

2009-09-25 11:11

1.       游标容器,存储SQL语句影响行数。

2.       游标类型隐式游标,显示游标,REF游标。其中,隐式游标和显示游标属于静态游标(运行前将游标与SQL语句关联),REF游标属于动态游标(运行时将游标与SQL语句关联)

3.       隐式游标: DML语句对应的游标,由Oracle自动管理,也称SQL游标。

q      隐式游标的属性有:

q      %FOUND – SQL 语句影响了一行或多行时为 TRUE

q      %NOTFOUND – SQL 语句没有影响任何行时为TRUE

q      %ROWCOUNT – SQL 语句影响的行数

q      %ISOPEN - 游标是否打开,始终为FALSE

举例说明:

declare

v_empno emp.empno %type:=7000;

begin

   update emp set ename='fxe' where empno=v_empno;

   if SQl%found then

      dbms_output.put_line(SQL%rowcount||'Delete Ok!');

   end if;

   if SQL%notfound then

      dbms_output.put_line('雇员编号'||v_empno||'不存在');

   end if;

end;

--------------------执行结果如下--------------------------

4. 显示游标操作:

                  (1)声明游标(关联SQL语句)    cursor+游标名 is/as sql语句

                  (2)打开游标(执行SQL语句,填充游标)    open+游标名

                  (3)提取游标的行    fetch 游标名 into 行类型变量

                  (4)关闭游标       close+游标名

举例说明:

declare

cursor emp_cur is select * from emp;

empRecord emp%rowtype;

begin

open emp_cur;

loop

    fetch emp_cur into empRecord;

    exit when emp_cur%notfound;

    dbms_output.put_line(empRecord.ename);

end loop;

close emp_cur;

end;

-----------------------------------执行结果如下图--------------------------------------------------------

5带参数的显示游标

:

declare

destination varchar2(20);

cursor emp_cur(dest varchar2)

   is select * from emp where empno=dest;

empRecord emp%rowtype;

begin

destination:=&empno;

open emp_cur(destination);

loop

fetch emp_cur into empRecord;

exit when emp_cur%notfound;

dbms_output.put_line(empRecord.ename);

end loop;

close emp_cur;

end;

----输入参数:7369

-----------------------------------------执行结果--------------------------------------------------

6.使用显示游标更新行

q      允许使用游标删除或更新活动集中的行

q      声明游标时必须使用 SELECT … FOR UPDATE语句

例:

declare

   old_sal number(4);

   emp_name varchar2(20);

   cursor emp_cur is select ename,sal from emp

                     where sal<1000

                     for update of sal;

begin

   open emp_cur;

   loop

     fetch emp_cur into emp_name,old_sal;

     exit when emp_cur%notfound;

     update emp set sal=1.1*old_sal

     where current of emp_cur;

     dbms_output.put_line('更新成功!');

   end loop;

end;

--------------------------执行结果---------------------------------------

7.循环游标

q     循环游标用于简化游标处理代码

q     当用户需要从游标中提取所有记录时使用

q     循环游标的语法如下:

     FOR <record_index> IN <cursor_name>

LOOP

       <executable statements>

END LOOP;

:

declare

cursor emp_cur is select empno,ename,sal from emp;

begin

for empRecord in emp_cur

loop

    dbms_output.put_line(empRecord.empno

    ||empRecord.ename||empRecord.sal);

end loop;

end;

--------------------执行结果--------------------------------

8.REF游标和游标变量

q      REF 游标和游标变量用于处理运行时动态执行的 SQL 查询

q      创建游标变量需要两个步骤:

q      声明 REF 游标类型

q      声明 REF 游标类型的变量

q      用于声明 REF 游标类型的语法为:

       TYPE <ref_cursor_name> IS REF CURSOR

       [RETURN <return_type>];

q      打开游标变量的语法如下:

        OPEN cursor_name FOR select_statement;

声明强类型的REF游标

type emp_cur is ref cursor return emp%rowtype;

empRecord emp_cur;

声明弱类型的REF游标

type emp_cur is ref cursor;

empRecord emp_cur;

:

DECLARE

    TYPE emp_cur IS REF CURSOR

          RETURN emp%ROWTYPE;

     empObj emp_cur;

    empRecord emp%ROWTYPE;

     BEGIN

    OPEN empObj FOR

      SELECT * FROM emp;

    loop

    FETCH empObj INTO empRecord;

    exit when empObj%notfound;

    dbms_output.put_line(empRecord.ename);

    end loop;

    CLOSE empObj;

     END;

--------------------------------------执行结果---------------------------------------


9.游标变量的优点和限制

q         游标变量的功能强大,可以简化数据处理。

q      游标变量的优点有:

q      可从不同的 SELECT 语句中提取结果集

q      可以作为过程的参数进行传递

q      可以进行赋值运算

q      使用游标变量的限制:

q      不能在程序包中声明游标变量

q      FOR UPDATE子句不能与游标变量一起使用

q      不能使用比较运算符

1.Oracle游标大全
    SELECT语句用于从数据库中查询数据,当在PL/SQL中使用SELECT语句时,要与INTO子句一起使用,查询的返回值被赋予INTO子句中的变量,变量的声明是在DELCARE中。SELECT             INTO语法如下:
      SELECT [DISTICT|ALL]{*|column[,column,...]}
      INTO (variable[,variable,...] |record)
      FROM {table|(sub-query)}[alias]
      WHERE............
     PL/SQLSELECT语句只返回一行数据。如果超过一行数据,那么就要使用显式游标(对游标的讨论我们将在后面进行),INTO子句中要有与SELECT子句中相同列数量的变量。INTO子句中也可以是记录变量。

%TYPE属性
      在PL/SQL中可以将变量和常量声明为内建或用户定义的数据类型,以引用一个列名,同时继承他的数据类型和大小。这种动态赋值方法是非常有用的,比如变量引用的列的数据类型和大小改变了,如果使用了%TYPE,那么用户就不必修改代码,否则就必须修改代码。

例:
 v_empno SCOTT.EMP.EMPNO%TYPE;
 v_salary EMP.SALARY%TYPE;
  不但列名可以使用%TYPE,而且变量、游标、记录,或声明的常量都可以使用%TYPE。这对于定义相同数据类型的变量非常有用。
     DELCARE
     V_A NUMBER(5):=10;
     V_B V_A%TYPE:=15;
     V_C V_A%TYPE;
     BEGIN
       DBMS_OUTPUT.PUT_LINE
       ('V_A='||V_A||'V_B='||V_B||'V_C='||V_C);
     END
     
     SQL>/
     V_A=10 V_B=15 V_C=
      PL/SQL procedure successfully completed.
      SQL>
      
 其他DML语句
     其它操作数据的DML语句是:INSERTUPDATEDELETELOCK TABLE,这些语句在PL/SQL中的语法与在SQL中的语法相同。我们在前面已经讨论过DML语句的使用这里就不再重复了。在DML语句中可以使用任何在DECLARE部分声明的变量,如果是嵌套块,那么要注意变量的作用范围。

例:
 CREATE OR REPLACE PROCEDURE FIRE_EMPLOYEE (pempno in number)
  AS
     v_ename EMP.ENAME%TYPE;
     BEGIN
      SELECT ename INTO v_ename
       FROM emp
       WHERE empno=p_empno;
       INSERT INTO FORMER_EMP(EMPNO,ENAME)
       VALUES (p_empno,v_ename);
       DELETE FROM emp
       WHERE empno=p_empno;
       UPDATE former_emp
       SET date_deleted=SYSDATE
       WHERE empno=p_empno;
       
     EXCEPTION
        WHEN NO_DATA_FOUND THEN
        DBMS_OUTPUT.PUT_LINE('Employee Number Not Found!');
     END

DML语句的结果
     当执行一条DML语句后,DML语句的结果保存在四个游标属性中,这些属性用于控制程序流程或者了解程序的状态。当运行DML语句时,PL/SQL打开一个内建游标并处理结果,游标是维护查询结果的内存中的一个区域,游标在运行DML语句时打开,完成后关闭。隐式游标只使用SQL%FOUND,SQL%NOTFOUND,SQL%ROWCOUNT三个属性.SQL%FOUND,SQL%NOTFOUND是布尔值,SQL%ROWCOUNT是整数值。

SQL%FOUNDSQL%NOTFOUND
     在执行任何DML语句前SQL%FOUNDSQL%NOTFOUND的值都是NULL,在执行DML语句后,SQL%FOUND的属性值将是:

. TRUE :INSERT
 . TRUE ELETEUPDATE,至少有一行被DELETEUPDATE.
 . TRUE :SELECT INTO至少返回一行
 当SQL%FOUNDTRUE,SQL%NOTFOUNDFALSE

SQL%ROWCOUNT
    在执行任何DML语句之前,SQL%ROWCOUNT的值都是NULL,对于SELECT             INTO语句,如果执行成功,SQL%ROWCOUNT的值为1,如果没有成功,SQL%ROWCOUNT的值为0,同时产生一个异常NO_DATA_FOUND.

SQL%ISOPEN
 SQL%ISOPEN是一个布尔值,如果游标打开,则为TRUE, 如果游标关闭,则为FALSE.对于隐式游标而言SQL%ISOPEN总是FALSE,这是因为隐式游标在DML语句执行时打开,结束时就立即关闭。

事务控制语句
     事务是一个工作的逻辑单元可以包括一个或多个DML语句,事物控制帮助用户保证数据的一致性。如果事务控制逻辑单元中的任何一个DML语句失败,那么整个事务都将回滚,在PL/SQL中用户可以明确地使用COMMITROLLBACKSAVEPOINT以及SET TRANSACTION语句。
      COMMIT语句终止事务,永久保存数据库的变化,同时释放所有LOCK,ROLLBACK终止现行事务释放所有LOCK,但不保存数据库的任何变化,SAVEPOINT用于设置中间点,当事务调用过多的数据库操作时,中间点是非常有用的,SET TRANSACTION用于设置事务属性,比如read-write和隔离级等。

显式游标
     当查询返回结果超过一行时,就需要一个显式游标,此时用户不能使用select into语句。PL/SQL管理隐式游标,当查询开始时隐式游标打开,查询结束时隐式游标自动关闭。显式游标在PL/SQL块的声明部分声明,在执行部分或异常处理部分打开,取数据,关闭。

使用游标
     这里要做一个声明,我们所说的游标通常是指显式游标,因此从现在起没有特别指明的情况,我们所说的游标都是指显式游标。要在程序中使用游标,必须首先声明游标。

声明游标
 语法:
     CURSOR cursor_name IS select_statement;

PL/SQL中游标名是一个未声明变量,不能给游标名赋值或用于表达式中。

例:
     DELCARE
     CURSOR C_EMP IS SELECT empno,ename,salary
     FROM emp
     WHERE salary>2000
     ORDER BY ename;
     ........
     BEGIN
     在游标定义中SELECT语句中不一定非要表可以是视图,也可以从多个表或视图中选择的列,甚至可以使用*来选择所有的列 。
     
 打开游标
 使用游标中的值之前应该首先打开游标,打开游标初始化查询处理。打开游标的语法是:
     OPEN cursor_name
        cursor_name是在声明部分定义的游标名。
     
 例:
      OPEN C_EMP;
         
 关闭游标
 语法:
      CLOSE cursor_name
     
 例:
      CLOSE C_EMP;

从游标提取数据
     从游标得到一行数据使用FETCH命令。每一次提取数据后,游标都指向结果集的下一行。语法如下:
      FETCH cursor_name INTO variable[,variable,...]
      对于SELECT定义的游标的每一列,FETCH变量列表都应该有一个变量与之相对应,变量的类型也要相同。

例:
    SET SERVERIUTPUT ON
    DECLARE
      v_ename EMP.ENAME%TYPE;
      v_salary EMP.SALARY%TYPE;
      CURSOR c_emp IS SELECT ename,salary FROM emp;
      BEGIN
        OPEN c_emp;
           FETCH c_emp INTO v_ename,v_salary;
             DBMS_OUTPUT.PUT_LINE('Salary of Employee'|| v_ename ||'is'|| v_salary);
           FETCH c_emp INTO v_ename,v_salary;
             DBMS_OUTPUT.PUT_LINE('Salary of Employee'|| v_ename ||'is'|| v_salary);
           FETCH c_emp INTO v_ename,v_salary;
             DBMS_OUTPUT.PUT_LINE('Salary of Employee'|| v_ename ||'is'|| v_salary);
        CLOSE c_emp;
      END
      
     这段代码无疑是非常麻烦的,如果有多行返回结果,可以使用循环并用游标属性为结束循环的条件,以这种方式提取数据,程序的可读性和简洁性都大为提高,下面我们使用循环重新写上面的程序:
 SET SERVERIUTPUT ON
 DECLARE
 v_ename EMP.ENAME%TYPE;
 v_salary EMP.SALARY%TYPE;
 CURSOR c_emp IS SELECT ename,salary FROM emp;
 BEGIN
 OPEN c_emp;
     LOOP
       FETCH c_emp INTO v_ename,v_salary;
       EXIT WHEN c_emp%NOTFOUND;
       DBMS_OUTPUT.PUT_LINE('Salary of Employee'|| v_ename ||'is'|| v_salary);
 END

记录变量
     定义一个记录变量使用TYPE命令和%ROWTYPE,关于%ROWsTYPE的更多信息请参阅相关资料。
     记录变量用于从游标中提取数据行,当游标选择很多列的时候,那么使用记录比为每列声明一个变量要方便得多。
     当在表上使用%ROWTYPE并将从游标中取出的值放入记录中时,如果要选择表中所有列,那么在SELECT子句中使用*比将所有列名列出来要安全得多。

例:
 SET SERVERIUTPUT ON
 DECLARE
 R_emp EMP%ROWTYPE;
 CURSOR c_emp IS SELECT * FROM emp;
 BEGIN
 OPEN c_emp;
    LOOP
      FETCH c_emp INTO r_emp;
      EXIT WHEN c_emp%NOTFOUND;
      DBMS_OUT.PUT.PUT_LINE('Salary of Employee'||r_emp.ename||'is'|| r_emp.salary);
    END LOOP;
 CLOSE c_emp;
 END;

%ROWTYPE也可以用游标名来定义,这样的话就必须要首先声明游标:

SET SERVERIUTPUT ON
 DECLARE
 CURSOR c_emp IS SELECT ename,salary FROM emp;
 R_emp c_emp%ROWTYPE;
 BEGIN
 OPEN c_emp;
 LOOP
     FETCH c_emp INTO r_emp;
     EXIT WHEN c_emp%NOTFOUND;
     DBMS_OUT.PUT.PUT_LINE('Salary of Employee'||r_emp.ename||'is'|| r_emp.salary);
 END LOOP;
 CLOSE c_emp;
 END;

带参数的游标
     与存储过程和函数相似,可以将参数传递给游标并在查询中使用。这对于处理在某种条件下打开游标的情况非常有用。它的语法如下:

CURSOR cursor_name[(parameter[,parameter],...)] IS select_statement;

定义参数的语法如下:
    Parameter_name [IN] data_type[{:=|DEFAULT} value]

  与存储过程不同的是,游标只能接受传递的值,而不能返回值。参数只定义数据类型,没有大小。
    另外可以给参数设定一个缺省值,当没有参数值传递给游标时,就使用缺省值。游标中定义的参数只是一个占位符,在别处引用该参数不一定可靠。

在打开游标时给参数赋值,语法如下:

OPEN cursor_name[value[,value]....];
 参数值可以是文字或变量。

例:
 DECALRE
 CURSOR c_dept IS SELECT * FROM dept ORDER BY deptno;
 CURSOR c_emp (p_dept VARACHAR2) IS
 SELECT ename,salary
 FROM emp
 WHERE deptno=p_dept
 ORDER BY ename
 r_dept DEPT%ROWTYPE;
 v_ename EMP.ENAME%TYPE;
 v_salary EMP.SALARY%TYPE;
 v_tot_salary EMP.SALARY%TYPE;
 BEGIN
 OPEN c_dept;
      LOOP
         FETCH c_dept INTO r_dept;
         EXIT WHEN c_dept%NOTFOUND;
         DBMS_OUTPUT.PUT_LINE('Department:'|| r_dept.deptno||'-'||r_dept.dname);
         v_tot_salary:=0;
         OPEN c_emp(r_dept.deptno);
             LOOP
                FETCH c_emp INTO v_ename,v_salary;
                EXIT WHEN c_emp%NOTFOUND;
                DBMS_OUTPUT.PUT_LINE('Name:'|| v_ename||' salary:'||v_salary);
                v_tot_salary:=v_tot_salary+v_salary;
             END LOOP;
         CLOSE c_emp;
         DBMS_OUTPUT.PUT_LINE('Toltal Salary for dept:'|| v_tot_salary);
      END LOOP;
 CLOSE c_dept;
 END;

游标FOR循环
 在大多数时候我们在设计程序的时候都遵循下面的步骤:
 1、打开游标
 2、开始循环
 3、从游标中取值
 4、检查那一行被返回
 5、处理
 6、关闭循环
 7、关闭游标
     可以简单的把这一类代码称为游标用于循环。但还有一种循环与这种类型不相同,这就是FOR循环,用于FOR循环的游标按照正常的声明方式声明,它的优点在于不需要显式的打开、关闭、取数据,测试数据的存在、定义存放数据的变量等等。游标FOR循环的语法如下:

FOR record_name IN
 (corsor_name[(parameter[,parameter]...)]
 | (query_difinition)
 LOOP
 statements
 END LOOP;

下面我们用for循环重写上面的例子:
 DECALRE
 CURSOR c_dept IS SELECT deptno,dname FROM dept ORDER BY deptno;
 CURSOR c_emp (p_dept VARACHAR2) IS
 SELECT ename,salary
 FROM emp
 WHERE deptno=p_dept
 ORDER BY ename
 v_tot_salary EMP.SALARY%TYPE;
 BEGIN
    FOR r_dept IN c_dept LOOP
      DBMS_OUTPUT.PUT_LINE('Department:'|| r_dept.deptno||'-'||r_dept.dname);
      v_tot_salary:=0;
      FOR r_emp IN c_emp(r_dept.deptno) LOOP
     DBMS_OUTPUT.PUT_LINE('Name:' || v_ename || 'salary:' || v_salary);  
     v_tot_salary:=v_tot_salary+v_salary;
      END LOOP;
      DBMS_OUTPUT.PUT_LINE('Toltal Salary for dept:'|| v_tot_salary);
 END LOOP;
 END;

在游标FOR循环中使用查询
     在游标FOR循环中可以定义查询,由于没有显式声明所以游标没有名字,记录名通过游标查询来定义。
 DECALRE
 v_tot_salary EMP.SALARY%TYPE;
 BEGIN
 FOR r_dept IN (SELECT deptno,dname FROM dept ORDER BY deptno) LOOP
      DBMS_OUTPUT.PUT_LINE('Department:'|| r_dept.deptno||'-'||r_dept.dname);
      v_tot_salary:=0;
      FOR r_emp IN (SELECT ename,salary
                FROM emp
                WHERE deptno=p_dept
                ORDER BY ename) LOOP
        DBMS_OUTPUT.PUT_LINE('Name:'|| v_ename||' salary:'||v_salary);
        v_tot_salary:=v_tot_salary+v_salary;
      END LOOP;
 DBMS_OUTPUT.PUT_LINE('Toltal Salary for dept:'|| v_tot_salary);
 END LOOP;
 END;

游标中的子查询
     语法如下:
     
 CURSOR C1 IS SELECT * FROM emp
 WHERE deptno NOT IN (SELECT deptno
    FROM dept
    WHERE dname!='ACCOUNTING');
 可以看出与SQL中的子查询没有什么区别。

游标中的更新和删除
     在PL/SQL中依然可以使用UPDATEDELETE语句更新或删除数据行。显式游标只有在需要获得多行数据的情况下使用。PL/SQL提供了仅仅使用游标就可以执行删除或更新记录的方法。
 UPDATEDELETE语句中的WHERE CURRENT OF子串专门处理要执行UPDATEDELETE操作的表中取出的最近的数据。要使用这个方法,在声明游标时必须使用FOR UPDATE子串,当对话使用FOR UPDATE子串打开一个游标时,所有返回集中的数据行都将处于行级(ROW-LEVEL)独占式锁定,其他对象只能查询这些数据行,不能进行UPDATEDELETESELECT...FOR            UPDATE操作。

语法:
     FOR UPDATE [OF [schema.]table.column[,[schema.]table.column]..
     [nowait]
     
     在多表查询中,使用OF子句来锁定特定的表,如果忽略了OF子句,那么所有表中选择的数据行都将被锁定。如果这些数据行已经被其他会话锁定,那么正常情况下ORACLE将等待,直到数据行解锁。

UPDATEDELETE中使用WHERE CURRENT OF子串的语法如下:

WHERE{CURRENT OF cursor_name|search_condition}

例:
 DELCARE
 CURSOR c1 IS SELECT empno,salary
 FROM emp
 WHERE comm IS NULL
 FOR UPDATE OF comm;
 v_comm NUMBER(10,2);
 BEGIN
    FOR r1 IN c1 LOOP
      IF r1.salary<500 THEN
        v_comm:=r1.salary*0.25;
      ELSEIF r1.salary<1000 THEN
        v_comm:=r1.salary*0.20;
      ELSEIF r1.salary<3000 THEN
        v_comm:=r1.salary*0.15;
      ELSE
          v_comm:=r1.salary*0.12;
      END IF;
    UPDATE emp;
    SET comm=v_comm
    WHERE CURRENT OF c1l;
    END LOOP;
 END

ORACLE中的游标汇总

c++/oracle/proc   2009-09-27 16:59   阅读42   评论0  

字号:     

游标(Cursor




游标(Cursor):用来查询数据库,获取记录集合(结果集)的指针,可以让开发者一次访问一行结果集,在每条结果集上作操作。

    游标可分为:

    <!--[if !supportLists]-->l         <!--[endif]-->静态游标:分为显式(explicit)游标和隐式(implicit)游标。





<!--[if !supportLists]-->l         <!--[endif]-->REF游标:是一种引用类型,类似于指针。----也称为动态游标


 

1、静态游标

1.1显式游标

定义格式:   

CURSOR 游标名 参数 )  [返回值类型]  IS    Select 语句

例子

set serveroutput on

declare

cursor emp_cur ( p_deptid in number) is




select * from employees where department_id = p_deptid;

 




l_emp employees%rowtype;

begin

 dbms_output.put_line('Getting employees from department 30');




open emp_cur(30);

 loop

  fetch emp_cur into l_emp;

  exit when emp_cur%notfound;

  dbms_output.put_line('Employee id '|| l_emp.employee_id || ' is');

  dbms_output.put_line(l_emp.first_name || ' ' || l_emp.last_name);

 end loop;

 close emp_cur;

 

 dbms_output.put_line('Getting employees from department 90');

open emp_cur(90);

 loop

 fetch emp_cur into l_emp;

  exit when emp_cur%notfound;

  dbms_output.put_line('Employee id '|| l_emp.employee_id || ' is');

  dbms_output.put_line(l_emp.first_name || ' ' || l_emp.last_name);

 end loop;

 close emp_cur;

end;

/

 

1.2隐式游标

不用明确建立游标变量,分两种:

1.在PL/SQL中使用DML语言,使用ORACLE提供的名为SQL的隐示游标。

举例:

declare

begin

 update departments    set  department_name=department_name where 1=2; dbms_output.put_line('update '|| sql%rowcount ||' records');

end;

2CURSOR FOR LOOP,用于for loop 语句

举例:

declare 

begin

 for my_dept_rec in ( select department_name, department_id from epartments)

 loop

  dbms_output.put_line(my_dept_rec.department_id || ' : ' || my_dept_rec.department_name);

 end loop;

end;

/

 

1.3游标常用属性:

%FOUND:变量最后从游标中获取记录的时候,在结果集中找到了记录。

%NOTFOUND:变量最后从游标中获取记录的时候,在结果集中没有找到记录。

%ROWCOUNT:当前时刻已经从游标中获取的记录数量。

%ISOPEN:是否打开。

 

Declare

 /* 定义静态游标 */

 Cursor emps is

 Select * from employees where rownum<6 order by 1;

 

 emp employees%rowtype;

 Row number :=1;

Begin

 Open emps;  /* 打开静态游标 */

 Fetch emps into emp;  /* 读取游标当前行 */

 

 Loop

  If emps%found then

   Dbms_output.put_line('Looping over record '||row|| ' of ' || emps%rowcount);

   Fetch emps into emp;

   Row := row + 1;

  Elseif emps%notfound then

   Exit; 

  End if;

 End loop;

 

 If emps%isopen then

  Close emps;   /*  关闭游标  */

 End if;

End;

/

 

 

显式和隐式游标的区别:




尽量使用隐式游标,避免编写附加的游标控制代码(声明,打开,获取,关闭),也不需要声明变量来保存从游标中获取的数据。


 


2REF CURSOR游标




动态游标,在运行的时候才能确定游标使用的查询。可以分为:





<!--[if !supportLists]-->l         <!--[endif]-->强类型(限制)(Strong REF CURSOR),规定返回类型





<!--[if !supportLists]-->l         <!--[endif]-->弱类型(非限制)(Weak REF CURSOR),不规定返回类型,可以获取任何结果集。


定义格式:

TYPE ref_cursor_name IS REF CURSOR [RETURN return_type]

 

例如:

Declare

  

Type refcur_t is ref cursor;

 Type emp_refcur_t is ref cursor return employee%rowtype;

Begin

 Null;

End;

/

 

强类型举例:

declare

 --声明记录类型

 type emp_job_rec is record(

  employee_id number,

  employee_name varchar2(50),

  job_title varchar2(30)

 );

 --声明REF CURSOR,返回值为该记录类型

 type emp_job_refcur_type  is  ref  cursor  return emp_job_rec;

 --定义REF CURSOR游标的变量

 emp_refcur  emp_job_refcur_type;

 

 emp_job  emp_job_rec;

begin

 /*  打开动态游标 */ 

 open emp_refcur for

  select e.employee_id, e.first_name || ' ' ||e.last_name "employee_name",

    j.job_title

  from employees e, jobs j

  where e.job_id = j.job_id and rownum < 11 order by 1;

 /*  取游标当前行  */

 fetch emp_refcur into emp_job;

 while emp_refcur%found loop

  dbms_output.put_line(emp_job.employee_name || '''s job is ');

  dbms_output.put_line(emp_job.job_title);

  fetch emp_refcur into emp_job;

 end loop;

end;

/

 

指定了retrun 类型,CURSOR变量的类型必须和return 类型一致。

例子:




CREATE OR REPLACE PACKAGE emp_data AS




TYPE empcurtyp IS REF CURSOR RETURN emp%rowtype;




--定义Strong REF CURSOR




PROCEDURE open_emp_cv(emp_cv IN OUT empcurtyp, choice INT);




--根据不同的choice选择不同的CURSOR




PROCEDURE retrieve_data(choice INT);




--通过调用procedure open_emp_cv,返回指定的结果集。




END emp_data;

 

 

CREATE OR REPLACE PACKAGE BODY emp_data AS

 

PROCEDURE open_emp_cv(emp_cv IN OUT empcurtyp, choice INT) IS




--emp_cv作为传入/传出的CURSOR PARAMETER

BEGIN

IF choice = 1 THEN




OPEN emp_cv FOR  SELECT * FROM emp WHERE empno < 7800;

ELSIF choice = 2 THEN




OPEN emp_cv FOR SELECT * FROM emp WHERE SAL < 1000;

ELSIF choice = 3 THEN




OPEN emp_cv  FOR SELECT * FROM emp WHERE ename like 'J%';

END IF;

END;

 

PROCEDURE retrieve_data(choice INT) IS




return_cv empcurtyp;




--定义传入open_emp_cvCURSOR变量




return_row emp%ROWTYPE;




invalid_choice EXCEPTION;

BEGIN

--调用 procedure OPEN_EMP_CV

open_emp_cv(return_cv, choice);

 

IF choice = 1 THEN




DBMS_OUTPUT.PUT_LINE('EMPLOYEES with empno less t han7800');

ELSIF choice = 2 THEN




DBMS_OUTPUT.PUT_LINE('EMPLOYEES with salary less than 1000');

ELSIF choice = 3 THEN




DBMS_OUTPUT.PUT_LINE('EMPLOYEES with name starts with ''J''');

ELSE




RAISE invalid_choice;

END IF;

 

LOOP




FETCH return_cv   INTO return_row;




EXIT WHEN return_cv%NOTFOUND;




DBMS_OUTPUT.PUT_LINE(return_row.empno || '--' || return_row.ename || '--' ||

return_row.sal);

END LOOP;

 

EXCEPTION




WHEN invalid_choice THEN




DBMS_OUTPUT.PUT_LINE('The CHOICE should be in one of (1,2,3)!');




WHEN OTHERS THEN




DBMS_OUTPUT.PUT_LINE('Errors in procedure RETRIEVE_DATA!');

END;

 

END emp_data;

 

 

执行:

SQL> EXEC emp_data.retrieve_data(1);

SQL> EXEC emp_data.retrieve_data(2);

SQL> EXEC emp_data.retrieve_data(3);

SQL> EXEC emp_data.retrieve_data(34);

 

使用Weak REF CURSOR例子

create or replace procedure open_cv(choice IN INT, return_cv OUT SYS_REFCURSOR) is




--参数return_cvweak REF CURSOR,利用SYS_CURSOR来定义




/*使用SYS_REFCURSOR可以使变量any_cv跟任何weak REF CURSOR类型匹配。  */

begin

if choice = 1 then




open return_cv for 'select * from emp';

elsif choice = 2 then




open return_cv for 'select * from dept';

end if;

end open_cv;

 

 

CREATE or replace procedure retrieve_data(choice IN INT) is

emp_rec emp%rowtype;

dept_rec dept%rowtype;

return_cv SYS_REFCURSOR;

invalid_choice exception;

 

BEGIN

if choice=1 then




dbms_output.put_line('employee information');




open_cv(1,return_cv); --调用procedure open_cv;




loop




fetch return_cv into emp_rec;




exit when return_cv%notfound;




dbms_output.put_line(emp_rec.empno||'-'||emp_rec.ename||'-'||emp_rec.sal);




end loop;

elsif choice=2 then




dbms_output.put_line('department information');




open_cv(2,return_cv);




 




loop




fetch return_cv into dept_rec;




exit when return_cv%notfound;




dbms_output.put_line(dept_rec.deptno||'-'||dept_rec.dname||'-'||dept_rec.loc);




end loop;

else




raise invalid_choice;

end if;

 

exception




when invalid_choice then




dbms_output.put_line('The CHOICE should be one of 1 and 2!');

when others then




dbms_output.put_line('Errors in procedure retrieve_data');

END retrieve_data;

 

 

执行:

SQL> exec retrieve_data(1);

SQL> exec retrieve_data(2);

 

 

REF CURSOR实现BULK功能

1. 可以加速INSERT, UPDATE, DELETE语句的执行,也就是用FORALL语句来替代循环语句。

2. 加速SELECT,用BULK COLLECT INTO 来替代INTO

 

SQL> create table tab2  as select empno ID, ename NAME, sal SALARY from emp where 1=2;

create or replace procedure REF_BULK is




/*  定义复杂类型 */




type empcurtyp  is ref cursor;




type idlist  is table of emp.empno%type;




type namelist  is table of emp.ename%type;




type sallist  is table of emp.sal%type;

  /* 定义变量  */




emp_cv  empcurtyp;




ids  idlist;




names namelist;




sals sallist;




row_cnt number;




begin




open emp_cv for select empno, ename, sal from emp;




fetch emp_cv  BULK COLLECT  INTO ids, names, sals;




--将字段成批放入变量中,此时变量是一个集合




close emp_cv;




 




for i in ids.first .. ids.last loop




dbms_output.put_line(' || ids(i) || ' || names(i) ||' salary=' || sals(i));




end loop;




 




FORALL  i  IN  ids.first .. ids.last




insert into tab2 values (ids(i), names(i), sals(i));




commit;




select count(*) into row_cnt from tab2;




dbms_output.put_line('-----------------------------------');




dbms_output.put_line('The row number of tab2 is ' || row_cnt);

end REF_BULK;

 

 

 

 

3cursor 和 ref cursor的区别




从技术底层看,两者是相同的。普通plsql cursor在定义时是静态的。而


Ref cursors可以动态打开。




例如下面例子:


Declare

typerc is ref cursor;

cursor c is select * from dual;

 

l_cursor rc;

begin

if ( to_char(sysdate,'dd') = 30 ) then

       open l_cursor for 'select * from emp';

elsif ( to_char(sysdate,'dd') = 29 ) then

       open l_cursor for select * from dept;

else

       open l_cursor for select * from dual;

end if;

open c;

end;

/




<!--[if !supportLists]-->l         <!--[endif]-->rc根据逻辑动态打开;而游标c定义好了只有就无法修改了。





<!--[if !supportLists]-->l         <!--[endif]-->ref cursor可以返回给客户端,cursor则不行。




<!--[if !supportLists]-->l         <!--[endif]-->cursor可以是全局的global ref cursor则必须定义在过程或函数中。





<!--[if !supportLists]-->l         <!--[endif]-->ref cursor可以在子程序间传递,cursor则不行。





<!--[if !supportLists]-->l         <!--[endif]-->cursor中定义的静态sqlref cursor效率高,所以ref cursor通常用在:向客户端返回结果集。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值