• oracle 游标


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

    游标可分为:

    l         静态游标:分为显式(explicit)游标和隐式(implicit)游标。

    l         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 departments)

     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;

     Elsif emps%notfound then

       Exit; 

     End if;

     End loop;

     If emps%isopen then

     Close emps;   /* 关闭游标 */

     End if;

    End;

    /

    显式和隐式游标的区别:

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

    2REF CURSOR游标

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

    l         强类型(限制)(Strong REF CURSOR),规定返回类型

    l         弱类型(非限制)(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(retu rn_cv, choice);

    IF choice = 1 THEN

    DBMS_OUTPUT.PUT_LINE('EMPLOYEES with empno less than 7800');

    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

    type rc 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;

    /

    l         rc根据逻辑动态打开;而游标c定义好了只有就无法修改了。

    l         ref cursor可以返回给客户端,cursor则不行。

    l         cursor可以是全局的global ref cursor则必须定义在过程或函数中。

    l         ref cursor可以在子程序间传递,cursor则不行。

    l         cursor中定义的静态sqlref cursor效率高,所以ref cursor通常用在:向客户端返回结果集。

  • 相关阅读:
    POJ-1189 钉子和小球(动态规划)
    POJ-1191-棋盘分割(动态规划)
    Java实现 LeetCode 730 统计不同回文子字符串(动态规划)
    Java实现 LeetCode 730 统计不同回文子字符串(动态规划)
    Java实现 LeetCode 729 我的日程安排表 I(二叉树)
    Java实现 LeetCode 729 我的日程安排表 I(二叉树)
    Java实现 LeetCode 729 我的日程安排表 I(二叉树)
    Java实现 LeetCode 728 自除数(暴力)
    Java实现 LeetCode 728 自除数(暴力)
    Java实现 LeetCode 728 自除数(暴力)
  • 原文地址:https://www.cnblogs.com/aloha/p/1366178.html
Copyright © 2020-2023  润新知