Oracle之PL/SQL学习笔记之游标(五)

Oracle之PL/SQL学习笔记之游标(五)


        在PL/SQL程序中,对于处理多行记录的事务常常使用游标来实现
sql


1. 游标的概念

        为了处理SQL语句,Oracle必须分配一片叫上下文(Context area)的区域来处理所必须的信息,其中包括要处理的行的数目,一个指向语句被分析之后的表示形式和指针以及查询的活动集(active set)。
数据库

        游标是一个指向上下文的句柄(handle)或指针。经过游标,PL/SQL能够控制上下文区和处理语句时上下文区会发生些什么事情。
express

       对于不一样的SQL语句,游标的使用状况不一样函数

SQL语句 游标
非查询语句 隐式的
结果是单行的查询语句 隐式的或显示的
结果是多行的查询语句 显示的


1.1 处理显示游标

 显示游标处理须要四个PL/SQL步骤:
oop




  • 定义游标: 就是定义一个游标名,以及与其相对应的SELECT语句。学习

    格式:  cursor cursor_name[(parameter[,parameter]...)] is select_statement;fetch

    游标参数只能为输入参数,其格式为spa

    parameter_name in datatype[{:=|DEFAULT} expression]指针

    注意:
    code

            在指定数据类型时,不能使用长度约束。如NUMBER(4)、CHAR(10)等都是错误的。

            定义游标时,不能有into自居。

  • 打开游标: 就是执行游标所对应的SELECT 语句,将其查询结果放入工做区,而且指针指向工做区的首部,表示游标结果集合。若是游标查询语句中带有FOR UPDATE选项,open语句还将锁定数据库表中游标结果集合对应的数据行。

     OPEN cursor_name[([parameter=>]value[,[parameter=>]value]...)]

    在向游标传递参数时,可使用与函数参数相同的传值方法,即位置表示法和名称表示法。PL/SQL程序不能用OPEN语句重复打开一个游标。

  • 提取游标数据: 就是检索结果集中的数据行,放入指定的输出变量中。

    格式: 

             FETCH cursor_name into {variable_list|record_variable};

    对该记录进行处理;

    继续处理,直到活动集合中没有记录;

  • 关闭游标: 当提取和处理完游标结果集合数据后,应及时关闭游标,以释放该游标所占用的系统资源,并使该游标的工做区变成无效,不能再使用FETCH语句取其中数据。关闭后的游标可使用OPEN语句从新打开。

    格式:

        close cursor;

    实例:

declare
  v_sal emp.sal%type;
  v_empno emp.empno%type;
  cursor emp_cursor is select sal,empno from emp where deptno=30;
begin
  open emp_cursor;--打开游标
  fetch emp_cursor into v_sal,v_empno;--提取游标
  while emp_cursor%found loop
     dbms_output.put_line('雇员编号:'||v_empno||',salary:'||v_sal);
     fetch emp_cursor into v_sal,v_empno;--提取游标
  end loop;
  close emp_cursor;--关闭游标
end;

   头脑风暴: 

    假如咱们的游标查询不少个字段的值,那么fetch emp_cursor into v_1,v_2,....是否是很麻烦呢?想象咱们的复合数据类型。


显示游标的属性

 

游标属性 值类型 说明
%FOUND 布尔型 当最近一次读取记录时成功返回,则值为true
%NOTFOUND 布尔型 与%FOUND相反
%ISOPEN 布尔型 当游标已打开时返回true
%ROWCOUNT 数字型 返回已从游标中读取的记录数


1.2 处理隐式游标

             隐式游标和显示游标有所差别,它虽然没有显示游标同样的可操做性,可是在实际的工做当中也常常用到。

1.2.1 隐式游标的特色

           每当容许SELECT或DML语句时,PL/SQL会打开一个隐式的游标。隐式游标不受用户的控制,这一点和显示游标有明显的不一样。下面列出隐式游标和显示游标的不一样处。

  •  隐式游标有PL/SQL自动管理;

  • 隐式游标的默认名称是SQL;

  • SELECT 或DML操做产生隐式游标;

  • 隐式游标的属性值始终是最新执行的SQL语句的;

    实例:


--隐式游标begin
---------------
--业务说明: 跟新指定员工编号员工的工资+11;若是存在该员工更新数据
-- 若是不存在,打印查无此人。
begin
  update emp set sal=sal+11 where empno=7788;
  if sql%notfound then 
    dbms_output.put_line('查无此人');
  end if;
---------------
--隐式游标end

     

   隐式游标的属性

属性名 值类型 说明
%ISOPEN true/false 该属性返回false,由Oracle本身控制
%FOUND true/false

此属性反应DML操做是否影响到了数据,SELECT INTO语句返回数据为true

%NOTFOUND true/false 与%FOUND相反
%ROWCOUNT number 该属性返回的是DML操做影响的行数。


2.  显示游标与循环控制


  案例:把雇员工资低于3000的调整到3000(loop)


-----------
--业务说明: 把雇员工资低于3000的调整到3000
-----------
declare
   v_sal emp.sal%type;--雇员工资,用于判断工资是否大于3000
   v_empno emp.empno%type;--雇员编号,用于更新工资
   cursor emp_cursor is select sal,empno from emp;--定义游标
begin
  --打开游标
  open emp_cursor;
  loop
    --获取数据
    fetch emp_cursor into v_sal,v_empno;
    exit when emp_cursor%notfound;
    if v_sal<3000 then 
      update emp set sal=3000 where empno=v_empno;
      dbms_output.put_line('雇员编号为:'||v_empno||'执行了更新操做');
      commit;
     end if;
  end loop;
  close emp_cursor;--关闭游标
end;


   咱们能够对以上案例进行修改,通常状况下使用cursor时是与for循环配置实用的:(for)

declare
   type emp_sal_empbno_record is record(
        sal emp.sal%type,--雇员工资,这儿须要注意,多个字段,须要用,隔开
        empno emp.empno%type--雇员编号
   );
   cursor emp_cursor is select sal,empno from emp;--定义游标
begin
  --for循环自动打开游标,获取每行的数据,关闭游标
  for emp_sal_empbno_record in emp_cursor loop
    if emp_sal_empbno_record.sal<3000 then 
      update emp set sal=3000 where empno=emp_sal_empbno_record.empno;
      dbms_output.put_line('雇员编号为:'||emp_sal_empbno_record.empno||'执行了更新操做');
      commit;
     end if;
  end loop;
end;


    分析:   从上面的代码中能够看到,for循环中,没有open cursor,fetch cursor,close cursor   可是这些都确切的发生了。

    for循环帮咱们在其内部处理了这些操做,简化了用户的操做,通常状况下for循环+cursor 堪称完美。(特殊状况除外),

   这儿还有一个须要注意,emp_sal_empbno_record 是记录类型,不是记录类型的变量。


   案例: 工资在0-3000 的加薪5%, 3000-4000 加薪3%,4000-5000 加薪2% 5000-n 加薪1%(while)


declare 
  type emp_record is record(
       sal emp.sal%type,
       empno emp.empno%type
  );--定义一个记录类型
  
  v_emp_record emp_record;--定义记录类型的变量
  v_temp number(4,2);--加薪比例
  cursor emp_cursor is select sal,empno from emp;--定义游标
  
begin
  open emp_cursor;--打开游标
  fetch emp_cursor into v_emp_record;--提取数据
  while emp_cursor%found loop
    if v_emp_record.sal<3000 then
      v_temp:=0.05;--须要注意赋值操做使用:=
    elsif v_emp_record.sal<4000 then
      v_temp:=0.03;
    elsif v_emp_record.sal<5000 then 
      v_temp:=0.02;
    else 
      v_temp:=0.01;
    end if; --记住 if语句结束后必定要有end if
    update emp set sal=sal*(1+v_temp) where empno=v_emp_record.empno;
    commit;   
    --提取数据,必须在while循环内部提取数据,否则就是死循环了
    fetch emp_cursor into v_emp_record;
  end loop;
end;

   

    注意: 两个案例,对应三种不一样的循环处理游标,你们作下对比。


3 使用cursor for loop(简化迭代结果集)

      对上面的案例进行改进:

declare 
  v_temp number(4,2);--加薪比例
  cursor emp_cursor is select sal,empno from emp;--定义游标
begin
  --从游标中取数据,自动打开,关闭,判读是否还有数据
  for v_emp in emp_cursor
    loop
      if v_emp.sal<3000 then 
        v_temp:=0.005;
      elsif v_emp.sal<4000 then
        v_temp:=0.03;
      elsif v_emp.sal<5000 then
        v_temp:=0.02;
      else 
        v_temp:=0.01;
      end if;
      update emp set sal=sal*(1+v_temp) where empno=v_emp.empno;
      commit;
    end loop;
end;


4 使用BULK COLLECT 和FOR 语句的游标

      游标中一般使用fetch.... into ... 语句取数据,这种方式是单条数据提取,在数据量很大的状况下执行效率不是很理想。而FETCH ... BULK COLLECT INTO 语句能够批量提取数据,

在数据量大的状况下它的执行效率比单条提取数据高。

declare 
  cursor emp_cursor is select * from emp;
  type emp_tab is table of emp%rowtype;
  v_emp_tab emp_tab;
begin 
  open emp_cursor;--打开游标
  loop
    fetch emp_cursor bulk collect into v_emp_tab limit 5;--使用limit显示一次取得记录数
    for i in 1 .. v_emp_tab.count loop
        dbms_output.put_line('雇员编号:'||v_emp_tab(i).empno||',雇员姓名:'||v_emp_tab(i).ename);
    end loop;
    exit when emp_cursor%notfound;
  end loop;
end;

   fetch emp_cursor bulk collect into v_emp_tab limit 5; 一次取5条记录到集合中,减小了取的次数。


5.  带参数的游标

---------带参数的游标begin-------------
--业务说明: 查询出某部门中雇员名中包含C的员工的姓名
declare 
    c_ename emp.ename%type:='%C%';
    v_ename emp.ename%type;--雇员姓名
    cursor emp_cursor(
           name varchar2,--雇员名,模糊查询
           dtno number--部门编号
           ) is select ename from emp where ename like name and deptno=dtno;
begin
  open emp_cursor(c_ename,10);
  loop 
    fetch emp_cursor into v_ename;
    exit when emp_cursor%NOTFOUND;
    dbms_output.put_line(v_ename||'的名字包含C');
  end loop;
  close emp_cursor;
end;
---------带参数的游标end---------------


  注意: 申明游标变量是,参数的类型,不能使用长度限制:

         错误的写法: varchar2(50),number(7,2)

相关文章
相关标签/搜索