游标(Cursor)是Oracle数据库中用于处理查询结果集的重要机制,它允许开发者逐行处理SQL语句返回的数据。
目录
一、游标基本概念
1. 游标定义
2. 游标分类
二、静态游标
(一)显式游标
【一】不带参数,普通的显示游标
1. 显式游标使用步骤
2. 语法
3. 显式游标的四个属性
4. 注意事项
5. %notfound 和普通循环一起用
6. %found 和while循环一起用
7. 基本示例
【二】带参数的显示游标
1. 语法结构
2. 示例代码
3. 练习
(二)隐式游标
1.隐式游标的四个属性
2. 示例代码
二、动态游标
【动态游标注意事项】
【强类型游标和弱类型游标区别】
【动态游标类型定义】
(一)强类型游标
(二)弱类型游标(SYS_REFCURSOR)
(三)动态游标
三、游标变量与批量处理
1. 游标变量
2. 批量提取(BULK COLLECT)
3. 批量处理与FORALL
四、游标最佳实践
五、高级游标技术
1. 可更新游标
2. 游标子查询
3. 游标表达式(12c+)
一、游标基本概念
1. 游标定义
游标是一个指向上下文区域的指针,用于处理SQL语句的执行结果。它提供了以下能力:
-
逐行访问结果集
-
跟踪当前处理的行
-
对结果集进行修改或删除操作
2. 游标分类
游标类型 | 描述 | 生命周期 | 控制方式 | |
---|---|---|---|---|
静态 | 隐式游标 | Oracle自动为每条SQL语句创建 | 单条SQL执行期间 | Oracle自动管理 |
显式游标 | 开发者显式定义 | 从OPEN到CLOSE | 开发者手动控制 | |
动态 | REF游标 | 动态游标,运行时确定 | 灵活控制 | 开发者控制 |
二、静态游标
(一)显式游标
显示的游标:在declare的部分用is显示了的游标
【一】不带参数,普通的显示游标
1. 显式游标使用步骤
(1)声明游标:定义游标及其关联的SELECT语句
(2)打开游标:执行查询,填充结果集
(3)提取数据:从结果集中获取行数据
(4)关闭游标:释放资源
2. 语法
-- 1. 声明游标
CURSOR cursor_name [(parameters)]
[RETURN return_type]
IS select_statement;
-- 2. 打开游标
OPEN cursor_name [(parameters)];
-- 3. 提取数据
FETCH cursor_name INTO variable_list;
-- 4. 关闭游标
CLOSE cursor_name;
-- 5. 举例
declare
cursor cur_name is select语句;---声明一个显示游标
begin
open cur_name;--打开游标
fetch cur_name into 变量;--赋值变量,提取记录
dbms_output.put_line()---打印
close cur_name;--关闭游标
end;
/
3. 显式游标的四个属性
属性 | 返回值 | 描述 | 说明 |
cursor_name%FOUND | 布尔值 | 如果最近一次 FETCH返回行则为TRUE | 游标的指针是否有值(有)对 (没有) 错 |
cursor_name%NOTFOUND | 布尔值 | 如果最近一次 FETCH未返回行则为TRUE | 游标的指针是否没值(有值)错, (没值)对,理论上可以返回空 在open之后fetch之前可以返回空 |
cursor_name%ROWCOUNT | 数值 | 到目前为止已提取的行数 | 游标的指针已经指了几行,返回数值, 但是要赋给变量才能显示 返回最近一次从游标读取的数据 |
cursor_name%ISOPEN | 布尔值 | 如果游标已打开则为TRUE | 判断是否打开游标(打开)对 (没有)错 |
4. 注意事项
首先声明一个游标,使用之前先打开游标,提取记录只能一行,可以多列
使用完游标要关闭游标,可以通过open打开游标继续使用
5. %notfound 和普通循环一起用
open→loop fetch→exit when %notfound→打印→end loop→close
【举例1】
declare
cursor cur_a is select * from emp;---声明一个显示游标
v_emp emp%rowtype;---声明变量
v1 varchar2(20);---声明变量
begin
open cur_a;--打开游标
loop/*普通循环*/
fetch cur_a into v_emp;/*赋值变量,抓取记录*/
exit when cur_a%notfound;/*和普通循环一起用*/
dbms_output.put_line(v_emp.ename);
end loop;/*结束循环*/
close cur_a;/*关闭游标*/
end;
/
-- LOOP循环语法
declare 部分;
begin
loop 要执行的语句;
exit when 退出的条件;
end loop;
end;
注意事项:进入循环不需要条件
6. %found 和while循环一起用
open→fetch→while %found loop→打印→fetch→end loop→close
【举例】
declare
cursor cur_a is select * from emp;
v_emp emp%rowtype;
begin
open cur_a;
fetch cur_a into v_emp;
while cur_a%found loop
dbms_output.put_line(v_emp.ename);
fetch cur_a into v_emp;
end loop;
close cur_a;
end;
7. 基本示例
DECLARE
-- 1. 声明游标
CURSOR emp_cursor IS
SELECT employee_id, last_name, salary
FROM employees
WHERE department_id = 10;
v_emp_id employees.employee_id%TYPE;
v_name employees.last_name%TYPE;
v_sal employees.salary%TYPE;
BEGIN
-- 2. 打开游标
OPEN emp_cursor;
-- 3. 提取数据
LOOP
FETCH emp_cursor INTO v_emp_id, v_name, v_sal;
EXIT WHEN emp_cursor%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(v_emp_id || ': ' || v_name || ', ' || v_sal);
END LOOP;
-- 4. 关闭游标
CLOSE emp_cursor;
END;
/
1、输出emp表工资前十名的员工姓名和薪资
declare
cursor cur_1 is
select ename, sal
from (select ename, sal from emp order by sal desc)
where rownum <= 10;
v_n emp.ename%type;
v_s emp.sal%type;
begin
open cur_1;
loop
fetch cur_1 into v_n, v_s;
exit when cur_1%notfound;
dbms_output.put_line('姓名:' || v_n || '薪资:' || v_s);
end loop;
close cur_1;
end;
/
-------------------
declare
cursor cur_1 is
select ename, sal
from (select ename, sal from emp order by sal desc)
where rownum <= 10;
v_n emp.ename%type;
v_s emp.sal%type;
begin
open cur_1;
fetch cur_1 into v_n, v_s;
while cur_1%found loop
dbms_output.put_line('姓名:' || v_n || '薪资:' || v_s);
fetch cur_1 into v_n, v_s;
end loop;
close cur_1;
end;
/
【二】带参数的显示游标
游标可以接受参数,使查询更加灵活:
1. 语法结构
CURSOR cursor_name (parameter1 datatype, parameter2 datatype, ...)IS select_statement;
declare
cursor cur_name(变量 类型) is select语句;---声明一个显示游标
begin
open cur_name(变量);--打开游标
fetch cur_name into 变量;--赋值变量,提取记录
close cur_name;--关闭游标
end;
2. 示例代码
DECLARE
-- 带参数的游标
CURSOR emp_cursor (p_dept_id NUMBER, p_min_sal NUMBER) IS
SELECT employee_id, last_name, salary
FROM employees
WHERE department_id = p_dept_id
AND salary >= p_min_sal;
-- 记录类型变量
v_emp_record emp_cursor%ROWTYPE;
BEGIN
-- 打开游标并传入参数
OPEN emp_cursor(10, 5000);
LOOP
FETCH emp_cursor INTO v_emp_record;
EXIT WHEN emp_cursor%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(v_emp_record.employee_id || ': ' ||
v_emp_record.last_name || ', ' ||
v_emp_record.salary);
END LOOP;
CLOSE emp_cursor;
END;
/
declare
cursor cur_1(v1 number) is select sal from emp where empno = v1; --声明一个带参数的显示游标
v_s emp.sal%type; --负责接收的变量
begin
open cur_1(&a); ---(7788)即为(v1 number)
fetch cur_1 into v_s;
dbms_output.put_line(v_s);
close cur_1;
end;
3. 练习
【1】输出工作是MANAGER的姓名、工资、;工作是SALESMAN的姓名、佣金;
工作是CLERK的姓名、入职日期。
declare
cursor cur_2(v1 varchar2) is
select * from emp where job = v1;
v_emp emp%rowtype;
begin
open cur_2('MANAGER');---
loop
fetch cur_2
into v_emp;
exit when cur_2%notfound;
dbms_output.put_line(v_emp.ename ||' '|| v_emp.sal);
end loop;
close cur_2;
open cur_2('SALESMAN');
loop
fetch cur_2
into v_emp;
exit when cur_2%notfound;
dbms_output.put_line(v_emp.ename ||' '|| v_emp.comm);
end loop;
close cur_2;
open cur_2('CLERK');
loop
fetch cur_2
into v_emp;
exit when cur_2%notfound;
dbms_output.put_line(v_emp.ename ||' '|| to_char(v_emp.hiredate,'yyyy-mm-dd'));
end loop;
close cur_2;
end;
/
【2】输出工作是MANAGER的姓名、工作、;工作是SALESMAN的姓名、佣金;
工作是CLERK的姓名、入职日期。
declare
cursor cur_xx(v_1 emp.job%type) is
select * from emp where job = v_1;
v_emp emp%rowtype;
begin
open cur_xx('MANAGER');
loop
fetch cur_xx
into v_emp;
exit when cur_xx%notfound;
dbms_output.put_line('员工姓名:'||v_emp.ename ||' '||'职位:'|| v_emp.job);
end loop;
close cur_xx;
open cur_xx('SALESMAN');
loop
fetch cur_xx
into v_emp;
exit when cur_xx%notfound;
dbms_output.put_line('员工姓名:'||v_emp.ename ||' '||'工资:'|| v_emp.sal);
end loop;
close cur_xx;
open cur_xx('CLERK');
loop
fetch cur_xx
into v_emp;
exit when cur_xx%notfound;
dbms_output.put_line('员工姓名:'||v_emp.ename ||' '||'入职日期:'||v_emp.hiredate);
end loop;
close cur_xx;
end;
/
-----------------------------------------------------------------------------------------
输出名字中包含%的人
select * from emp where ename like '%'||v1||'%' or ename like'%'||v_2||'%'
【3】打印名字中包含A的人数,包含E的平均工资,包含o的总工资
declare
cursor cur_3(v1 varchar2) is---先让cur_3有了selecte
select count(sal), avg(sal), sum(sal)
from emp
where ename like '%' || v1 || '%';----注意学习这种方法
--声明一个带参数的显示游标cur_3
v_2 number;
v_3 number;
v_4 number;
----------------添加负责接收的变量
begin
open cur_3('A');
loop
fetch cur_3---又从cur_3里提取值赋值给变量v_2,v_3,v_4
into v_2, v_3, v_4;
exit when cur_3%notfound;
dbms_output.put_line(v_2);
end loop;
close cur_3;
---------------
open cur_3('E');
loop
fetch cur_3
into v_2, v_3, v_4;
exit when cur_3%notfound;
dbms_output.put_line(v_3);
end loop;
close cur_3;
----------------
open cur_3('O');
loop
fetch cur_3
into v_2, v_3, v_4;
exit when cur_3%notfound;
dbms_output.put_line(v_4);
end loop;
close cur_3;
----------------
end;
(二)隐式游标
主要应用于增加删除更新数据,Oracle为每条DML语句自动创建隐式游标,当执行SQL语句的时候,这个游标是处理该语句的工作区域。在使用的时候要使用隐式游标的默认名称SQL。
1.隐式游标的四个属性
属性 | 返回 | 描述 | 说明 |
cursor_name%FOUND | 布尔值 | 如果DML操作影响至少一行返回TRUE | 游标的游标中是否有值,返回最近一次的结果,成功(对)否则(错) |
cursor_name%NOTFOUND | 布尔值 | 如果DML操作未影响任何行返回TRUE | 游标的游标中是否有值,返回最近一次的结果,成功(错) |
cursor_name%ROWCOUNT | 数值 | 返回DML操作影响的行数 | 返回最近一次从游标中读取到的记录--数值类型 |
cursor_name%ISOPEN | 布尔值 | 对隐式游标总是返回FALSE | 判断是否打开游标。永远返回错 |
补充:闪回不仅可以闪回删除前的数据,也可以返回之前某一时间点的数据
2. 示例代码
BEGIN
-- 更新操作
UPDATE employees SET salary = salary * 1.1
WHERE department_id = 10;
-- 检查隐式游标属性
IF SQL%FOUND THEN
DBMS_OUTPUT.PUT_LINE('更新了 ' || SQL%ROWCOUNT || ' 条记录');
END IF;
-- 删除操作
DELETE FROM temp_employees WHERE employee_id = 9999;
IF SQL%NOTFOUND THEN
DBMS_OUTPUT.PUT_LINE('未删除任何记录');
END IF;
END;
----------------------------------------------------------------
----------------------------------------------------------------
/
begin
delete from emp001 where deptno=10;--3
--dbms_output.put_line('删除了'||sql%rowcount||'行');
delete from emp001 where deptno=20;--5
if sql%found
then
dbms_output.put_line('删除了'||sql%rowcount||'行') ;
end if;
end;
/
----------------------------------------------------------------
begin
--dbms_output.put_line('删除了'||sql%rowcount||'行');
delete from emp001 where deptno in (10,20);--5
if sql%found
then
dbms_output.put_line('删除了'||sql%rowcount||'行') ;
end if;
end;
/
----------------------------------------------------------------
----------------------------------------------------------------
declare
v_name csm_product.product_name%type;
begin
insert into test_t values(1);
if sql%found then
dbms_output.put_line('收到影响的行数为:'||sql%rowcount);
end if;
rollback;
end;
----------------------------------------------------------------
----------------------------------------------------------------
declare
cursor v_cur is select * from test_t
begin
if v_cur%isopen then
dbms_output.put_line('游标已经打开');
else
dbms_output.put_line('游标未打开');
end if;
open v_cur;
if v_cur%isopen then
dbms_output.put_line('游标已经打开');
end if;
close v_cur;
insert into test_t valuse(1);
if sql%found then
dbms_output.put_line('执行成功,影响的行数:'||sql%rowcount)
elsif sql%notfound then
dbms_output.put_line('执行失败');
end if;
rollback;
end;
二、动态游标
【动态游标注意事项】
1、使用动态游标必须声明游标类型
2、只要列的格式相同,可以同时打开多表
【强类型游标和弱类型游标区别】
1、强类型游标有return,open时查询得到的结果要和return后的表的数据类型、结构、顺序一致。
2、没有return,open时查询的结果比较自由,fetch的时候into给的变量要和SQL查询的类型结构数量一致。
【动态游标类型定义】
TYPE cursor_type IS REF CURSOR [RETURN return_type];
(一)强类型游标
DECLARE
TYPE emp_cursor_type IS REF CURSOR RETURN employees%ROWTYPE;
emp_cursor emp_cursor_type;
v_emp employees%ROWTYPE;
BEGIN
OPEN emp_cursor FOR SELECT * FROM employees WHERE department_id = 20;
LOOP
FETCH emp_cursor INTO v_emp;
EXIT WHEN emp_cursor%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(v_emp.employee_id || ': ' || v_emp.last_name);
END LOOP;
CLOSE emp_cursor;
END;
/
declare
type cur_name_ref is ref cursor return emp%rowtype; --emp可以自定义,只是声明了一个类型
cur_1 cur_name_ref;
v_emp emp%rowtype;
begin
--------------------------------------------------------------------
open cur_1 for select * from emp;-------------同时打开多表---------
fetch cur_1
into v_emp;
dbms_output.put_line(v_emp.deptno || v.emp.ename || v_emp.job);
close cur_1;
--------------------------------------------------------------------
open cur_1 for select * from emp001;---------同时打开多表----------
fetch cur_1
into v_emp;
dbms_output.put_line(v_emp.deptno || v.emp.ename || v_emp.job);
close cur_1;
end;
(二)弱类型游标(SYS_REFCURSOR)
DECLARE
emp_cursor SYS_REFCURSOR;
v_emp_id employees.employee_id%TYPE;
v_emp_name employees.last_name%TYPE;
BEGIN
-- 打开第一个查询
OPEN emp_cursor FOR
SELECT employee_id, last_name
FROM employees
WHERE department_id = 10;
DBMS_OUTPUT.PUT_LINE('部门10员工:');
LOOP
FETCH emp_cursor INTO v_emp_id, v_emp_name;
EXIT WHEN emp_cursor%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(v_emp_id || ': ' || v_emp_name);
END LOOP;
CLOSE emp_cursor;
-- 重用游标执行不同查询
OPEN emp_cursor FOR
SELECT department_id, department_name
FROM departments;
DBMS_OUTPUT.PUT_LINE('所有部门:');
LOOP
FETCH emp_cursor INTO v_emp_id, v_emp_name; -- 重用变量
EXIT WHEN emp_cursor%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(v_emp_id || ': ' || v_emp_name);
END LOOP;
CLOSE emp_cursor;
END;
/
declare
type cur_name_ref is ref cursor;---没有return,和强类型的区别就在于此
cur_name cur_name_ref;
v_emp emp%rowtype;
begin
open cur_name for select * from emp;-------------同时打开多表---------------
fetch cur_name
into v_emp;
dbms_output.put_line(v_emp.deptno || v.emp.ename || v_emp.job);
close cur_1;---注意游标开一次,关一次,不然一直开着耗内存。
open cur_name for select * from emp001;--------同时打开多表---------------
fetch cur_name
into v_emp;
dbms_output.put_line(v_emp.deptno || v.emp.ename || v_emp.job);
close cur_1;
end;
【练习题】
1、输出工作是MANAGER的姓名、工资;工作是SALESMAN的姓名、佣金;
工作是CLERK的姓名、入职日期。
declare
type cur_1_ref is ref cursor; ---没有return
cur_1 cur_1_ref;
v1 varchar2(20);
v2 number;
v3 date;
begin
open cur_1 for
select ename, sal from emp where job = 'MANAGER';
loop
fetch cur_1
into v1, v2;
exit when cur_1%notfound;
dbms_output.put_line(v1 || v2);
end loop;
open cur_1 for
select ename, sal from emp where job = 'SALESMAN';
loop
fetch cur_1
into v1, v2;
exit when cur_1%notfound;
dbms_output.put_line(v1 || v2);
end loop;
open cur_1 for
select ename, hiredate from emp where job = 'CLERK';
loop
fetch cur_1
into v1, v3;
exit when cur_1%notfound;
dbms_output.put_line(v1 || v3);
end loop;
end;
/
--用弱类型游标,打印emp名字中包含A的人数,dept部门编号的平均数
--salgrade 第三等级的hisal
declare
type cur_name_ref is ref cursor;
cur_gam cur_name_ref;
v_1 number;
begin
open cur_gam for
select count(ename) from emp where ename like '%A%';----单列单行不用循环
fetch cur_gam
into v_1;
dbms_output.put_line('名字包含A的人数:' || v_1);
close cur_gam;
open cur_gam for
select avg(deptno) from dept;----单列单行不用循环
fetch cur_gam
into v_1;
dbms_output.put_line('部门编号的平均数:' || v_1);
close cur_gam;
open cur_gam for
select hisal from salgrade where grade = 3;----单列单行不用循环
fetch cur_gam
into v_1;
dbms_output.put_line('第三等级的hisal:' || v_1);
close cur_gam;
end;
--------如果多行多列,加入循环的写法
declare
type cur_name_ref is ref cursor;
cur_gam cur_name_ref;
v_1 number;
begin
open cur_gam for
select count(ename) from emp where ename like '%A%';
loop
fetch cur_gam
into v_1;
exit when cur_gam%notfound;
dbms_output.put_line('名字包含A的人数:'||v_1);
end loop;
close cur_gam;
-----------------------
open cur_gam for
select avg(deptno) from dept;
loop
fetch cur_gam
into v_1;
exit when cur_gam%notfound;
dbms_output.put_line('部门编号的平均数:'||v_1);
end loop;
close cur_gam;
-----------------------------
open cur_gam for
select hisal from salgrade where grade=3;
loop
fetch cur_gam
into v_1;
exit when cur_gam%notfound;
dbms_output.put_line('第三等级的hisal:'||v_1);
end loop;
close cur_gam;
end;
(三)动态游标
【语法】
declare
cur_name sys_refcursor;
v1 number;
v2 varchar2(20);
begin
open cur_name for
select empno, ename from emp where empno = 7788;
fetch cur_name
into v1, v2;
dbms_output.put_line(v1 || v2);
close cur_name;
--------------
open cur_name for
select empno, ename from emp where deptno = 20;
fetch cur_name
into v1, v2;
dbms_output.put_line(v1 || v2);
close cur_name;
end;
【练习题】
1、打印10部门的人员姓名和部门地址,打印20部门的工资和工资等级
declare
cur_1 sys_refcursor;
ve varchar2(20);
vc varchar2(20);
vs number;
vg number;
begin
open cur_1 for
select e.ename,d.loc from emp e,dept d where e.deptno=d.deptno and d.deptno=10;
loop
fetch cur_1
into ve, vc;
exit when cur_1%notfound;
dbms_output.put_line(ve ||' '|| vc);
end loop;
open cur_1 for
select e.sal, s.grade from emp e,salgrade s where e.sal between s.losal and s.hisal and e.deptno=20;
loop
fetch cur_1
into vs, vg;
exit when cur_1%notfound;
dbms_output.put_line(vs ||' '|| vg);
end loop;
close cur_1;
end;
/
2、用动态游标里面的动态游标,打印emp名字中包含A的人数,dept部门编号的平均数,salgrade 第三等级的hisal
declare
cur_2 sys_refcursor;
v1 number;
begin
open cur_2 for
select count(ename) from emp where ename like '%A%';
fetch cur_2
into v1;
dbms_output.put_line('名字包含A的人数:' || v1);
close cur_2;
----------------------
open cur_2 for
select avg(deptno) from dept;
fetch cur_2
into v1;
dbms_output.put_line('部门编号的平均数:' || v1);
close cur_2;
-----------------------
open cur_2 for
select hisal from salgrade where grade = 3;
fetch cur_2
into v1;
dbms_output.put_line('第三等级的hisal:' || v1);
close cur_2;
end;
/
三、游标变量与批量处理
1. 游标变量
DECLARE
TYPE emp_cursor_type IS REF CURSOR;
emp_cursor emp_cursor_type;
PROCEDURE process_employees (p_cursor IN emp_cursor_type) IS
v_emp employees%ROWTYPE;
BEGIN
LOOP
FETCH p_cursor INTO v_emp;
EXIT WHEN p_cursor%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(v_emp.employee_id || ': ' || v_emp.last_name);
END LOOP;
END;
BEGIN
OPEN emp_cursor FOR SELECT * FROM employees WHERE department_id = 10;
process_employees(emp_cursor);
CLOSE emp_cursor;
END;
/
2. 批量提取(BULK COLLECT)
DECLARE
CURSOR emp_cursor IS
SELECT employee_id, last_name, salary
FROM employees
WHERE department_id = 10;
-- 定义集合类型
TYPE emp_id_array IS TABLE OF employees.employee_id%TYPE;
TYPE name_array IS TABLE OF employees.last_name%TYPE;
TYPE sal_array IS TABLE OF employees.salary%TYPE;
v_ids emp_id_array;
v_names name_array;
v_sals sal_array;
BEGIN
OPEN emp_cursor;
-- 批量提取数据
FETCH emp_cursor BULK COLLECT INTO v_ids, v_names, v_sals;
CLOSE emp_cursor;
-- 处理批量数据
FOR i IN 1..v_ids.COUNT LOOP
DBMS_OUTPUT.PUT_LINE(v_ids(i) || ': ' || v_names(i) || ', ' || v_sals(i));
END LOOP;
END;
/
3. 批量处理与FORALL
DECLARE
TYPE id_array IS TABLE OF employees.employee_id%TYPE;
TYPE sal_array IS TABLE OF employees.salary%TYPE;
v_ids id_array := id_array(101, 102, 103, 104, 105);
v_new_sals sal_array;
BEGIN
-- 批量查询
SELECT salary BULK COLLECT INTO v_new_sals
FROM employees
WHERE employee_id IN (SELECT COLUMN_VALUE FROM TABLE(v_ids));
-- 批量更新
FORALL i IN 1..v_ids.COUNT
UPDATE employees
SET salary = v_new_sals(i) * 1.1
WHERE employee_id = v_ids(i);
COMMIT;
DBMS_OUTPUT.PUT_LINE('成功更新 ' || SQL%ROWCOUNT || ' 条记录');
END;
/
四、游标最佳实践
1. 及时关闭游标:避免资源泄漏
BEGIN
OPEN emp_cursor;
-- 处理数据
EXCEPTION
WHEN OTHERS THEN
IF emp_cursor%ISOPEN THEN
CLOSE emp_cursor;
END IF;
RAISE;
END;
2. 使用游标FOR循环:简化代码,自动处理打开/关闭
3. 批量操作:对大结果集使用BULK COLLECT和FORALL
4. 参数化游标:提高代码重用性
5. 限制返回行数:避免内存问题
FETCH emp_cursor BULK COLLECT INTO v_emps LIMIT 1000;
6. 使用合适的作用域:在包中定义常用游标
7. 性能监控:检查游标SQL的执行计划
五、高级游标技术
1. 可更新游标
DECLARE
CURSOR emp_cursor IS
SELECT employee_id, last_name, salary
FROM employees
WHERE department_id = 10
FOR UPDATE OF salary NOWAIT;
v_raise_percent NUMBER := 0.1;
BEGIN
FOR emp_rec IN emp_cursor LOOP
UPDATE employees
SET salary = salary * (1 + v_raise_percent)
WHERE CURRENT OF emp_cursor;
END LOOP;
COMMIT;
END;
/
2. 游标子查询
DECLARE
CURSOR dept_cursor IS
SELECT d.department_id, d.department_name,
CURSOR(
SELECT employee_id, last_name
FROM employees
WHERE department_id = d.department_id
) AS emp_cursor
FROM departments d
WHERE d.location_id = 1700;
v_emp_cursor SYS_REFCURSOR;
v_emp_id employees.employee_id%TYPE;
v_emp_name employees.last_name%TYPE;
BEGIN
FOR dept_rec IN dept_cursor LOOP
DBMS_OUTPUT.PUT_LINE('部门: ' || dept_rec.department_name);
v_emp_cursor := dept_rec.emp_cursor;
LOOP
FETCH v_emp_cursor INTO v_emp_id, v_emp_name;
EXIT WHEN v_emp_cursor%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(' ' || v_emp_id || ': ' || v_emp_name);
END LOOP;
CLOSE v_emp_cursor;
END LOOP;
END;
/
3. 游标表达式(12c+)
DECLARE
CURSOR dept_cursor IS
SELECT d.department_id,
d.department_name,
CURSOR(
SELECT e.employee_id, e.last_name
FROM employees e
WHERE e.department_id = d.department_id
) AS emp_cur
FROM departments d;
BEGIN
FOR dept_rec IN dept_cursor LOOP
DBMS_OUTPUT.PUT_LINE('部门: ' || dept_rec.department_name);
FOR emp_rec IN dept_rec.emp_cur LOOP
DBMS_OUTPUT.PUT_LINE(' 员工: ' || emp_rec.last_name);
END LOOP;
END LOOP;
END;
/
游标是Oracle PL/SQL中处理结果集的核心机制,掌握各种游标技术可以显著提高数据库应用程序的效率和灵活性。