显示游标获取数据

来源:互联网 发布:留学生眼中的日本知乎 编辑:程序博客网 时间:2024/04/29 06:44
--在Oracle中,当我们从PL/SQL中执行一条select语句时,Oracle RDBMS(关系数据库管理系统)会为该语句在SGA的共享池中分配一个私有SQL区,
--同时在将查询结果集放入系统全局区SGA中,以提高访问和修改结果集的性能。
--私有SQL区包含了该语句的信息以及结果集的信息。Oracle中使用游标作为指针来引用该私有工作区。
--借助游标,PL/SQL程序可以控制私有工作区和语句处理过程中游标的变化。


--PL/SQL中的游标分为隐示游标(执行一个返回单行的select into 时,Oracle自动处理相关操作)
--和显示游标(手动处理相关操作,获取多行,重用性强)两种类型。


--一个典型的查询操作步骤如下:
--解析:确保sql语句有效,然后决定执行计划
--绑定:将PL/SQL程序中的变量与绑定变量关联。
--打开:打开游标
--执行:在SQL引擎中运行SQL语句。
--提取:从游标的结果集中获取下一行。每次提取后,PL/SQL将指针在结果集中向前移动。当使用示游标时,如果没有提取到行,那么fetch命令不做任何操作,也不引发错误。
--关闭:关闭游标,释放游标所占用的所有内存。


--游标的状态(例如是否打开,获取了多少行数据等)可以使用游标的属性来获取。




-----使用显示游标的一个完整示例
declare
cursor cur_emp is select * from scott.emp; --定义一个游标
row_emp  cur_emp%rowtype;  --基于游标定义一个记录
begin
open cur_emp;  --打开游标
fetch cur_emp into  row_emp;--从游标中提取一行添加到记录中


while cur_emp%found    --如果记录成功获取,返回true


  loop
  dbms_output.put_line(row_emp.empno || '------'||row_emp.ename);
  fetch cur_emp into row_emp;--从游标中提取一行添加到记录中
  end loop;
  
  close cur_emp;--- 关闭游标
end;


---在SELECT语句中提供了FOR UPDATE子句,在我们执行COMMIT或ROLLBACK之前,其他人就只能读这些记录,而不能修改这些记录。
---下面是在游标中使用FOR UPDATE子句的两个示例
--1
cursor emp_cur is
select empno, ename,sal from scott.emp where job='manager' for update;
--2
cursor emp_cur is
select empno, ename ,sal from emp where job ='manager' for update  of sal;
 --of 后面是查询字段列表中的一个列名.
 
 --如果多表查询的select语句中使用FOR UPDATE 子句 那么只有在for update 子句中国引用了某个表的列时,该表中的行才会被锁住
 --例如,在下面的示列中,for update   子句不会锁住表dept中的任何行;
 --这是因为for update 子句只引用了表emp中的列sal,而表dept中的列一个也没用被引用。
 select * from scott.dept
 cursor emp_cur
 select d.dname,e.ename,e.sal from scott.dept d, scott.emp e where e.job='manager'and d.deptno =e.deptno for update of e.sal;
 
 
 
 ----for update 子句的OF 列表并不限制我们只能更改列出的列。
 ----锁还是放在所有被影响的行上面。
 ----OF 列表只是让我们更清楚我们需要更改什么。
 ----没有带OF关键字,那么数据库就会锁住from 子句中列出的所有表的被影响的行。
 
 
 
 -----FOR UPDATE子句后添加一个NOWAIT 关键字,用于告诉Oracle 如果表已经被其他用户锁住,就不需要等待...
 -----一旦一个带有FOR UPDATE 的游标被打开后,游标结果集中的所有的行都会被锁住,直到当前会话提交了commit语句保存修改,
 -----或使用rollback语句取消修改为止。
 -----提交或回滚后, 行上的锁就被释放。因此,在commit或rollback后,我们就不能在对forupdate游标的执行fetch了。
 -----如果我们在用select。。for update 定义的游标中提取记录后,需要执行commit或rollback,必须在循环或者条件逻辑中加入EXIT代码中断继续从游标中提取记录。
 
 ----where current of子句引用的是游标
 
 
 ---下面是一个使用显示游标更改数据的完整示例,实现了为雇员中所有部门经理加薪1000的功能;
 
 select *from scott.emp
 
 declare
 --定义一个游标emp_cur,其结果集为雇员中所有的部门经理,游标打开后会把这些记录加锁
 cursor emp_cur  is
 select empno,ename,sal from scott.emp where job='MANAGER' for update;
emp_row emp_cur%rowtype;
begin
 open emp_cur;
  loop
    fetch emp_cur into emp_row;
     if emp_cur%notfound  --如果游标中没有记录,或者到了最后一条记录,就退出循环
       then
         exit;
     else
      --给每位部门经理加薪1000
     update scott.emp set sal=sal+1000 where current of emp_cur;
     end if;
   end loop;
  commit;
 close emp_cur;
end;
 
 
 
 ----游标for循环
 --for 记录 in 游标名
 --loop
 --执行语句
 --end loop;
 --记录是不需要我们显示定义的,它根据指定的游标名,用%rowtype属性隐式地定义的。(游标for循环仅用在需要处理游标中每一条记录时)
 select *from scott.emp
 
 
declare
    cursor emp_cur is
     select empno,ename,sal from scott.emp where job='MANAGER' for update ;
begin 
       for emp_row in emp_cur 
         loop
           update scott.emp set sal=sal-1000 where current of emp_cur;
         end loop;
       commit;
end;
--我们可以看到游标for循环确实很好地简化了游标的开发,我们不再需要声明记录,不再需要open,fetch和close语句,
--不再需要使用%found属性检测是否到最后一条记录,这一切Oracle隐式地帮我们完成了。




---。。bulk collect 
--可以在select into\fetch into\returning into子句中使用bulk collect
---bulk collect的一些必须记住的规则。


--现在我们用select。。。bulk collect into改写上面的显式游标示例,代码如下:
declare
 type emp_table_type is table of scott.emp%rowtype index by  binary_integer;
 emp_table emp_table_type; 
begin


 select * bulk collect into emp_table from scott.emp;
 for i in 1..emp_table.count
  loop
    dbms_output.put_line(emp_table(i).empno ||'--'||emp_table(i).ename);
  end loop;
end;


---当然也可以在显式游标的fetch 。。into中应用 bulk collect子句。示例代码如下:
declare
  cursor cur_emp is select *from scott.emp;
  type row_emp_type is table of cur_emp%rowtype index by binary_integer;
  row_emp row_emp_type;
begin
  open cur_emp;
  fetch cur_emp  bulk collect into row_emp;
  for i in 1..row_emp.count
    loop
    dbms_output.put_line(row_emp(i).empno||'--'||row_emp(i).ename||'--'||row_emp(i).job);
    end loop;  
    close cur_emp;
end;

0 0