在 PL/SQL 程序中,对于处理多行记录的事务经常使用游标来实现。
为了处理 SQL 语句, ORACLE 必须分配一片叫上下文( context area )的区域来处理所必需的信息,其中包括要处理的行的数目,一个指向语句被分析以后的表示形式的指针以及查询的活动集(active set)。
游标是一个指向上下文的句柄( handle)或指针。通过游标, PL/SQL 可以控制上下文区和处理语句时上下文区会发生些什么事情。
1.处理显示游标
处理显示游标有4个步骤:
(1)定义游标:
CURSOR cursor_name [(parameter [, parameter]…) ] IS 子查询 ;
在指定数据类型时,不能使用长度约束。如 NUMBER(4)、 CHAR(10) 等都是错误的。
(2)打开游标:
OPEN cursor_name [([parameter =>] value [, [parameter =>] value]…)];
就是执行游标所对应的 SELECT 语句,将其查询结果放入工作区,并且指针指向工作区的首部,标识游标结果集合。如果游标查询语句中带有 FOR UPDATE 选项, OPEN 语句还将锁定数据库表中游标结果集合对应的数据行。
(3)提取游标数据:
FETCH cursor_name INTO { variable_list | record_variable };
检索结果集合中的数据行,放入指定的输出变量中。
对该记录进行处理;
继续处理,直到活动集合中没有记录;
(4)关闭游标:
CLOSE cursor_name;
当提取和处理完游标结果集合数据后,应及时关闭游标,以释放该游标所占用的系统资源,并使该游标的工作区变成无效,不能再使用 FETCH 语句取其中数据。
关闭后的游标可以使用 OPEN 语,句重新打开。
注:定义的游标不能有 INTO 子句。
示例1:
declare --1. 定义游标 cursor salary_cursor is select salary from employees where department_id = 80; v_salary employees.salary%type; begin --2. 打开游标 open salary_cursor; --3. 提取游标 fetch salary_cursor into v_salary; --4. 对游标进行循环操作: 判断游标中是否有下一条记录 while salary_cursor%found loop dbms_output.put_line('salary: ' || v_salary); fetch salary_cursor into v_salary; end loop; --5. 关闭游标 close salary_cursor; end;
示例2:
/* 利用游标, 调整公司中员工的工资: 工资范围 调整基数 0 - 5000 5% 5000 - 10000 3% 10000 - 15000 2% 15000 - 1% */ declare --定义游标 cursor emp_sal_cursor is select salary, employee_id from employees; --定义基数变量 temp number(4, 2); --定义存放游标值的变量 v_sal employees.salary%type; v_id employees.employee_id%type; begin --打开游标 open emp_sal_cursor; --提取游标 fetch emp_sal_cursor into v_sal, v_id; --处理游标的循环操作 while emp_sal_cursor%found loop --判断员工的工资, 执行 update 操作 --dbms_output.put_line(v_id || ': ' || v_sal); if v_sal <= 5000 then temp := 0.05; elsif v_sal<= 10000 then temp := 0.03; elsif v_sal <= 15000 then temp := 0.02; else temp := 0.01; end if; --dbms_output.put_line(v_id || ': ' || v_sal || ', ' || temp); update employees set salary = salary * (1 + temp) where employee_id = v_id; fetch emp_sal_cursor into v_sal, v_id; end loop; --关闭游标 close emp_sal_cursor; end;
使用SQL中的 decode 函数
update employees set salary = salary * (1 + (decode(trunc(salary/5000), 0, 0.05,
1, 0.03,
2, 0.02,
0.01)))
2.游标属性
%FOUND |
布尔型属性,当最近一次读记录时成功返回,则值为 TRUE; |
%NOTFOUND | 布尔型属性,与%FOUND 相反; |
%ISOPEN | 布尔型属性,当游标已打开时返回 TRUE; |
%ROWCOUNT | 数字型属性,返回已从游标中读取的记录数。 |
3. 游标的 FOR 循环
FOR index_variable IN cursor_name[value[, value]…] LOOP -- 游标数据处理代码 END LOOP;
PL/SQL 语言提供了游标 FOR 循环语句,自动执行游标的 OPEN、 FETCH、 CLOSE 语句和循环语句的功能
如果在游标查询语句的选择列表中存在计算列,则必须为这些计算列指定别名后才能通过游标 FOR 循环语句中的索引变量来访问这些列数据。
注:不要在程序中对游标进行人工操作;不要在程序中定义用于控制 FOR 循环
示例1:
/* 利用游标, 调整公司中员工的工资: 工资范围 调整基数 0 - 5000 5% 5000 - 10000 3% 10000 - 15000 2% 15000 - 1% */ declare --定义游标 cursor emp_sal_cursor is select salary, employee_id id from employees; --定义基数变量 temp number(4, 2); begin --处理游标的循环操作 for c in emp_sal_cursor loop --判断员工的工资, 执行 update 操作 --dbms_output.put_line(v_id || ': ' || v_sal); if c.salary <= 5000 then temp := 0.05; elsif c.salary <= 10000 then temp := 0.03; elsif c.salary <= 15000 then temp := 0.02; else temp := 0.01; end if; --dbms_output.put_line(v_id || ': ' || v_sal || ', ' || temp); update employees set salary = salary * (1 + temp) where employee_id = c.id; end loop; end;
4.带参数的游标
declare --定义游标 cursor emp_sal_cursor(dept_id number, sal number) is select salary + 1000 sal, employee_id id from employees where department_id = dept_id and salary > sal; --定义基数变量 temp number(4, 2); begin --处理游标的循环操作 for c in emp_sal_cursor(sal => 4000, dept_id => 80) loop --判断员工的工资, 执行 update 操作 --dbms_output.put_line(c.id || ': ' || c.sal); if c.sal <= 5000 then temp := 0.05; elsif c.sal <= 10000 then temp := 0.03; elsif c.sal <= 15000 then temp := 0.02; else temp := 0.01; end if; dbms_output.put_line(c.sal || ': ' || c.id || ', ' || temp); --update employees set salary = salary * (1 + temp) where employee_id = c.id; end loop; end;
5.隐式游标
显式游标主要是用于对查询语句的处理,尤其是在查询结果为多条记录的情况下;而对于非查询语句,如修改、删除操作,则由ORACLE系统自动地为这些操作设置游标并创建其工作区,这些由系统隐含创建的游标称为隐式游标, 隐式游标的名字为 SQL,这是由 ORACLE 系统定义的。对于隐式游标的操作,如定义、打开、取值及关闭操作,都由 ORACLE系统自动地完成,无需用户进行处理。用户只能通过隐式游标的相关属性,来完成相应的操作。在隐式游标的工作区中,所存放的数据是与用户自定义的显示游标无关的、最新处理的一条SQL语句所包含的数据。
调用格式为: SQL%
--更新指定员工 salary(涨工资 10),如果该员工没有找到,则打印”查无此人”信息 begin update employees set salary = salary + 10 where employee_id = 1005; if sql%notfound then dbms_output.put_line('查无此人!'); end if; end;