与游标一样,游标变量也是一个指向多行查询结果集合中当前数据行的指针。但与游标不同的是,游标变量是动态的,而游标是静态的。游标只能与指定的查询相连,即固定指向一个查询的内存处理区域,而游标变量则可与不同的查询语句相连,它可以指向不同查询语句的内存处理区域(但不能同时指向多个内存处理区域,在某一时刻只能与一个查询语句相连),只要这些查询语句的返回类型兼容即可。
声明游标变量
游标变量为一个指针,类型是 REF CURSOR,所以在声明游标变量类型之前必须先定义游标变量类型。在 PL/SQL中,可以在块、子程序和包的声明区域内定义游标变量参照类型。
语法格式如下:
TYPE ref_type_name IS REF CURSOR [ RETURN return_type];
ref_type_name 为新定义的游标变量类型名称,retuen_type 为游标变量的返回值类型,它必须为记录变量。
在定义游标变量类型时,可以采用强类型定义和弱类型定义两种。强类型定义必须指定游标变量的返回值类型,而弱类型定义则不说明返回值类型。
强类型游标变量示例如下:
obclient> DECLARE
-> TYPE rec_emp_job IS RECORD(
-> employee_id employees.employee_id%TYPE,
-> employee_name employees.first_name%TYPE,
-> job_id employees.job_id%TYPE
-> );
-> TYPE emp_job_refcur_type IS REF CURSOR RETURN rec_emp_job;
-> refcur_emp emp_job_refcur_type;
-> emp_job rec_emp_job;
-> BEGIN
-> OPEN refcur_emp FOR
-> SELECT employee_id, first_name, job_id
-> FROM employees
-> ORDER BY department_id;
-> FETCH refcur_emp INTO emp_job;
-> WHILE refcur_emp%FOUND LOOP
-> DBMS_OUTPUT.PUT_LINE(emp_job.employee_id||': '||emp_job.employee_name||' is a '||emp_job.job_id);
-> FETCH refcur_emp INTO emp_job;
-> END LOOP;
-> END;
-> /
Query OK, 0 rows affected (0.14 sec)
200: Jennifer is a AD_ASST
202: Pat is a MK_REP
201: Michael is a MK_MAN
119: Karen is a PU_CLERK
118: Guy is a PU_CLERK
...
弱类型游标变量示例如下:
obclient> DECLARE
-> TYPE rec_emp_job IS RECORD(
-> employee_id employees.employee_id%TYPE,
-> employee_name employees.first_name%TYPE,
-> job_id employees.job_id%TYPE
-> );
-> -- 这里emp_job_refcur_type并没有定义返回类型。
-> TYPE emp_job_refcur_type IS REF CURSOR;
-> refcur_emp emp_job_refcur_type;
-> emp_job rec_emp_job;
-> BEGIN
-> OPEN refcur_emp FOR
-> SELECT employee_id, first_name, job_id
-> FROM employees
-> ORDER BY department_id;
-> FETCH refcur_emp INTO emp_job;
-> WHILE refcur_emp%FOUND LOOP
-> DBMS_OUTPUT.PUT_LINE(emp_job.employee_id||': '||emp_job.employee_name||' is a '||emp_job.job_id);
-> FETCH refcur_emp INTO emp_job;
-> END LOOP;
-> END;
-> /
Query OK, 0 rows affected (0.14 sec)
200: Jennifer is a AD_ASST
202: Pat is a MK_REP
201: Michael is a MK_MAN
119: Karen is a PU_CLERK
...
弱类型游标并没有返回值,是个通用的类型,可以通过系统内置的类型 SYS_REFCURSOR 来直接定义。所以上面的例子可以改写成如下示例:
obclient> DECLARE
-> TYPE rec_emp_job IS RECORD(
-> employee_id employees.employee_id%TYPE,
-> employee_name employees.first_name%TYPE,
-> job_id employees.job_id%TYPE
-> );
-> -- 定义refcur_emp类型为SYS_REFCURSOR
-> refcur_emp SYS_REFCURSOR;
-> emp_job rec_emp_job;
-> BEGIN
-> OPEN refcur_emp FOR
-> SELECT employee_id, first_name, job_id
-> FROM employees
-> ORDER BY department_id;
-> FETCH refcur_emp INTO emp_job;
-> WHILE refcur_emp%FOUND LOOP
-> DBMS_OUTPUT.PUT_LINE(emp_job.employee_id||': '||emp_job.employee_name||' is a '||emp_job.job_id);
-> FETCH refcur_emp INTO emp_job;
-> END LOOP;
-> END;
-> /
Query OK, 0 rows affected (0.13 sec)
200: Jennifer is a AD_ASST
202: Pat is a MK_REP
201: Michael is a MK_MAN
119: Karen is a PU_CLERK
118: Guy is a PU_CLERK
...
注意
对于弱类型的游标变量,PL 引擎在编译时并不知道变量的类型,需要在执行期间进行绑定。 这意味着程序在执行期间会消耗更多的资源和时间。
SYS_REFCURSOR 类型的游标变量支持重用。
使用动态 SQL 定义游标变量
动态 SQL 是一种高级编程方式,指在 PL 程序运行时生成和执行 SQL。 游标变量同样支持动态绑定, 使用 OPEN… FOR 子句通过字符串来定义定标。 下面例子里,游标 cv 通过 query_2 这个字符串变量来定义。
obclient> DECLARE
-> cv SYS_REFCURSOR; -- cursor variable
-> query_2 VARCHAR2(200) :=
-> 'SELECT * FROM employees
'> ORDER BY job_id';
-> v_employees employees%ROWTYPE; -- record variable row of table
-> BEGIN
-> OPEN cv FOR query_2;
-> LOOP
-> FETCH cv INTO v_employees;
-> EXIT WHEN cv%NOTFOUND;
-> DBMS_OUTPUT.PUT_LINE(v_employees.employee_id||'-'||v_employees.first_name);
-> END LOOP;
-> CLOSE cv;
-> END;
-> /
Query OK, 0 rows affected (0.14 sec)
206-William
205-Shelley
200-Jennifer
102-Lex
101-Neena
109-Daniel
...
关于动态 SQL 的详细信息,请参见动态 SQL。
使用游标变量循环获取记录集的每行记录
使用游标变量循环获取记录集的每行记录的方法如下:
在声明部分
定义动态游标类型:
TYPE cursor_type IS REF CURSOR [ RETURN return_type ];
声明一个变量使用上面的动态游标类型:
cursor_variable cursor_type;
声明一个记录用于保存游标返回的记录:
record_name return_type;
在执行部分
打开游标变量绑定到特定查询:
OPEN cursor_variable FOR query;
从游标记录集中获取行数据。每次一行,结果下面格式的 LOOP 语句一起使用可以获取多行数据。
LOOP
FETCH cursor_variable INTO record_name;
EXIT WHEN cursor_variable%NOTFOUND;
statement;
[ statement; ]...
END LOOP;
关闭游标:CLOSE cursor_name;
或者重新打开游标变量,绑定到另外一个查询(会自动关闭当前查询的游标),重复前面的步骤。
示例如下:使用游标变量循环获取记录集的每行记录
delimiter /
CREATE OR REPLACE PACKAGE pkg_ware_mgmt AS
TYPE TYPE_REFCURSOR_WARE IS REF CURSOR RETURN ware%ROWTYPE;
PROCEDURE sp_record_print;
END;
/
delimiter ;
delimiter /
CREATE OR REPLACE PACKAGE BODY pkg_ware_mgmt
AS
PROCEDURE sp_record_print_by_record(p_cursor IN TYPE_REFCURSOR_WARE)
AS
rec_ware ware%ROWTYPE;
BEGIN
LOOP
dbms_output.put_line('Try to fetch a row from the ref cursor .');
FETCH p_cursor INTO rec_ware;
EXIT WHEN p_cursor%NOTFOUND ;
dbms_output.put_line('Print a record :');
dbms_output.put_line('W_ID : ' || rec_ware.w_id
|| ', W_YTD : ' || rec_ware.w_ytd
|| ', W_TAX : ' || rec_ware.w_tax
|| ', W_NAME : ' || rec_ware.w_name
|| ', W_STREET_1 : ' || rec_ware.w_street_1
|| ', W_STREET_2 : ' || rec_ware.w_street_2
|| ', W_CITY : ' || rec_ware.w_city
|| ', W_STATE : ' || rec_ware.w_state
|| ', W_ZIP : ' || rec_ware.w_zip )
;
dbms_output.put_line('');
END LOOP;
dbms_output.put_line('Processed ' || p_cursor%ROWCOUNT || ' rows. ');
END;
PROCEDURE sp_record_print
IS
cursor_ware TYPE_REFCURSOR_WARE ;
BEGIN
OPEN cursor_ware FOR SELECT * FROM ware ORDER BY w_id ;
dbms_output.put_line('Open a ref cursor using query at ware.');
sp_record_print_by_record(cursor_ware);
CLOSE cursor_ware;
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('Raise an unkown exception !');
END ;
END;
/
delimiter ;
obclient> set serveroutput on;
Query OK, 0 rows affected (0.00 sec)
obclient> call pkg_ware_mgmt.sp_record_print();