Oracle学习笔记(5) 在PLSQL中使用游标获取数据

    这是第五章的学习笔记,学习完第四章的数据库操作和事务之后,开始要学习游标的使用了……,希望大家能多给俺一些支持啊!
    编程时使用的工具是PLSQL Developer 7.1.4

隐式游标
隐式游标的属性等在第四章笔记中已记录了一部分
如果要确保属性指向想要的SQL语句,那么就在SQL语句执行后,立即将属性值保存到一个本地变量中
用存储过程来实现这种效果:
先定义一个存储过程remove_from_emp ,用于从雇员中删除一个员工
在存储过程的参数中 in 表示输入,out 表示输出
create or replace procedure remove_from_emp(empno_in in employee.empno%type)
is
begin
   delete from employee where empno = empno_in;
   dbms_output.put_line('删除了' || sql%rowcount || '条记录!');
end;
/
-- 定义一个存储过程来调用存储过程remove_from_emp,并保存隐式游标的属性值
create or replace procedure show_emp_count is
   i_count integer;
   i_numfound pls_integer;
begin
   select count(*) into i_count from employee;
   -- 将属性值做一个快照
   i_numfound := sql%rowcount;
   -- 注意employee表中没有编号为99的员工
   -- 调用存储过程remove_from_emp
   remove_from_emp(1);
   -- 现在可以用前一条语句的属性值输出
   dbms_output.put_line(i_numfound);
end;
/

显式游标(PL/SQL块中使用显示游标的本个步骤:声明、打开、提取记录、关闭)
在程序包规范中声明一个游标类型(我们可以在PL/SQL块或者包的定义部分声明游标类型)
create or replace package types
is
type emp_cur5 is ref cursor;
end;
/
declare
   -- 定义一个不带参数的游标emp_cur1,游标的结果是employee表中员工的集合
   cursor emp_cur1 is select * from employee;
   -- 定义一个带参数的游标emp_cur2,游标的结果集是匹配由游标传递过来的员工编号的员工姓名和加入公司的时间
   cursor emp_cur2 (empno_in in number) is select name,hiredate from employee where empno = empno_in;
   -- 定义一个带有return 子句的游标,游标的结果集是员工编号为1的employee表中所有的列
   cursor emp_cur3 return employee%rowtype is select * from employee where empno = 1; 

如果想通过游标更新数据,必须在select 语句后加上for update 子句,
该select 语句涉及的所有行都会被锁住
如果是加上了for update of 列名,那么只有在for update 子句中引用了某个表的列时,
该表中的行才会被锁住,of 列表并不限制只能更改列给出的列,它只让我们知道要更改什么
可以在for update 子句后添加一个nowait 关键字,用于告诉Oracle如果表已经被其他用户
锁住,就不需要等待了,这样控制权会立即返回给我们的程序,如果没有nowait子句进程就
会阻塞,直到表可用(commit或rollback)为止
可以在Select into、Fetch into、Returning into子句中使用Bulk Collect将数据输出到集合中
   cursor emp_cur4 (empno_in in number) is select name,salary from employee where empno = empno_in for update;
   -- 应该总是将游标行提取到用%rowtype定义的记录中,这样更灵活(表字段改变了不需要更改fetch语句)
   emp_row1 employee%rowtype;
   -- 基于游标定义一个记录
   emp_row2 emp_cur2%rowtype;
   emp_row3 emp_cur3%rowtype;
   emp_row4 emp_cur4%rowtype;
   n_salary number(10,2);
   n_empno employee.empno%type := &员工编号:;
   
   -- 游标变量和ref cursor
   -- 定义ref cursor 类型的游标变量
   -- 创建一个强类型的引用游标类型
   type emp_ref_type1 is ref cursor return employee%rowtype;
   -- 创建一个弱类型的引用游标类型(弱类型的游标变量比强类型的游标变量更灵活)
   type emp_ref_type2 is ref cursor;
   -- 定义实际的游标变量
   emp_ref1 emp_ref_type1;
   emp_ref2 emp_ref_type2;
   -- 从Oracle9i 开始提供了一个名为sys_refcursor的预定义的Oracle系统游标,
   -- 它相当于弱类型游标,使用它不需要定义游标变量
   sys_cursor sys_refcursor;
   -- 定义一个行类型的集合
   type emp_table_type is table of employee%rowtype index by binary_integer;
   emp_table emp_table_type;
   type emp_info_type is record(name employee.name%type,job employee.job%type);
   emp_info emp_info_type;
begin

打开游标:open 显式游标名 (参数列表)
一旦打开了显式游标,就可以从游标中提取记录,直到没有记录留在游标中
打开游标时,PL/SQL就开始执行该游标的Select 查询,但是实际上并不返回任何行,
返回行的任务是由后面的Fetch(读取)…… into(赋值给)……  语句完成的:
fetch 游标名 into 记录或变量列表
   open emp_cur1;
   -- 提取记录
   -- 如果游标只返回一行可以用if、loop或for来判断获得数据,如果游标返回多行可以用loop或for来循环获得数据
   loop
      fetch emp_cur1 into emp_row1;
      exit when emp_cur1%notfound;
      dbms_output.put_line('员工' || emp_row1.name || '的工资是:' || emp_row1.salary);
   end loop;
   -- 关闭游标并释放资源
   close emp_cur1;
   -- 打开带参数的游标
   -- 游标for 循环能很好的简化游标的开发,我们不再需要声明记录,不再需要Open、Fetch和Close语句
   -- 也不再需要%found属性检测记录,一切Oracle隐式的帮我们完成了
   for emp_row2 in emp_cur2(n_empno) loop
      dbms_output.put_line('员工' || emp_row2.name || '加入公司的时间是 ' || emp_row2.hiredate);
   end loop;
   -- 打开带return 子句的游标
   open emp_cur3;
   fetch emp_cur3 into emp_row3;
   if emp_cur3%found then
      dbms_output.put_line('员工' || emp_row3.name || '其职位是' || emp_row3.job || ',加入公司的时间是 ' || emp_row3.hiredate);
   end if;
   close emp_cur3;
   -- 打开带for update 子句的游标,将指定编号的员工工资增加500元
   open emp_cur4(n_empno);
   fetch emp_cur4 into emp_row4;

where current of 游标名 子句能很容易的修改最近提取的数据行(也就是当前游标指向的位置),
这样的好处是,如果表表结构发生了改变,我们只需要更改Select语句的Where子句即可,而不
需要更新每个SQL语句
   if emp_cur4%found then
      update employee set salary = salary + 500 where current of emp_cur4; --returning salary into n_salary;
   end if;
   commit;
   n_salary := emp_row4.salary + 500;
   dbms_output.put_line('员工' || emp_row4.name || '原来的工资是' || emp_row4.salary || '元,增加工资后现在的工资是' || n_salary || '元');
   close emp_cur4;
   
   -- 打开强类型游标变量
   open emp_ref1 for select * from employee order by salary;
   -- 在游标变量中提取数据
   fetch emp_ref1 bulk collect into emp_table;
   for i in 1..emp_table.count loop
       dbms_output.put_line(emp_table(i).name || '   本月工资 ' || emp_table(i).salary);
   end loop;
   -- 关闭游标变量
   close emp_ref1;
   -- 打开弱类型游标变量
   open emp_ref2 for select name,job from employee;
   loop
      fetch emp_ref2 into emp_info;
      exit when emp_ref2%notfound;
      dbms_output.put_line(emp_info.name || '的工作是 ' || emp_info.job); 
   end loop;
   close emp_ref2;
   -- 打开Oracle系统游标
   open sys_cursor for select name,hiredate from employee order by hiredate desc;
   loop
      fetch sys_cursor into emp_info;
      exit when sys_cursor%notfound;
      dbms_output.put_line(emp_info.name || '加入公司的时间是 ' || emp_info.job); 
   end loop;
   close sys_cursor;
exception
   when NO_DATA_FOUND then dbms_output.put_line('查询不到员工编号为' || n_empno || '的员工!');
   when TOO_MANY_ROWS then dbms_output.put_line('数据完整性错误,员工编号' || n_empno || '重复!');
   when OTHERS then dbms_output.put_line('PL/SQL执行错误!' || sqlerrm);
end;
/

你可能感兴趣的:(数据结构,oracle,sql,编程)