oracle数据库笔记1PL/SQL基础3游标

游标的概念

在ORACLE中, 游标实际上就是在内存中开辟的一个专用SQL区的工作区
用于存储执行SQL查询命令返回的信息

PL/SQL中有两种类型的光标,显示光标和隐式光标

对于返回多行的查询,为了单个地处理每一行,必须显示地定义一个光标

显示光标的操作

该操作包括定义光标,打开光标,取数据,关闭光标

可用CURSOR OPEN,FETCH和CLOSE控制光标

1.定义游标

在PL/SQL块的说明部分定义游标

定义形式:

 CURSOR    游标名称    IS
  
     SELECT    语句;

DECLARE
  cursor c1 is
  select ename,deptno
  from emp
  where sal>2000;
  ...
BEGIN
  NULL;
END;

定义游标时可以带参数(形参),给查询传递需要的参数值

游
标的参数在其查询中,可以在出现常数的地方使用

定义时只要把参数名及其数据类型与游标名称一起说明

传递的参数值可用来在查询中控制查询的结果

带参数的游标定义形式:

CURSOR   游标名(参数[,参数]…) IS 
 
     SELECT  语句;

参数名[IN] 数据类型[{ := | DEFAULT}值]

游标参数的使用范围局限于游标即仅在游标定义中指定的

查询命令内引用。游标参数的值在打开(OPEN)时提供

CURSOR C2(median numbre) IS
  SELECT job,ename
  from emp
  where sal>median;

2. 打开游标

在PL/SQL块中执行语句部分BEGIN之后,使用时要先打开游标 (用OPEN 命令) 。

打开游标的形式:

OPEN    游标名[(实参数)]; 

OPEN  C1 ;
OPEN  C2 (300);

执行打开游标语句,系统执行游标定义中的查询命令, 标识活
动集

对带参数的游标,OPEN语句对于游标说明中每一个形式参 数必须有相应的实在参数

若形式参数具有缺省值, 则可不需要有相应的实在参数

游标形参必须是IN参数, 所以不能将值返回给实在参数

在OPEN语句中实在参数与形式参数之间的联系有两种方法

位置表示法:实参与形参个数, 位置一一对应:

OPEN    C2 (300);

命名表示法:以形参=>实参的形式表示:

ey: CURSOR C3(my_name char(10),my_comm number) IS
    SELECT ...

OPEN C3('ATTLEY',300);
OPEN C3(my_name=>'ATTLEY',my_comm=>300);

3. 利用游标取值

游标被打开后,要用FETCH 语句把查询的记录 取到 PL/SQL
程序块的变量中

语句形式:

FETCH   游标名  INTO  变量名表;

FETCH语句检索工作区(活动集)中的行,每次只能取一行。

每执行一次FETCH语句, 游标指针下移一行,等待取下一行记录

变量名表中变量的数量和数据类型与查询返回的列的数量与类型相匹配, 位置对应

FETCH C1 INTO my_ename,my_deptno;

注意:

(1) 使用FETCH语句前, 必须先打开游标。
(2)在FETCH 语句执行过程中, 游标指针只能逐行下移,不能回退。

4. 关闭游标

 CLOSE    游标名称

处理完游标工作区中的记录行后, 就以关闭游标了, 游标关闭后,系统释放与该游标相关的所有资源,并使该游标的工作区失效

若此时对游标再执行FETCH语句则出错    但可重新打开游标

对未关闭
的 游标执行打开也会发生错误

ey:取出部门号为10的雇员姓名和工资

DECLARE 
  v_no emp.deptno%tyoe:=10;
  v_sal emp.sal%type;
  v_ename emp.ename%type;
  n number(3);
  CURSOR C1 IS 
  select ename,sal from emp
  where deptno=v_no;
BEGIN
  select count(*) into n from emp
  where deptno=v_no;
  open c1;
  for i in 1..n loop
    fetch ci into v_ename,v_sal;
    dbms_output.put_line(v_ename || to_char(v_sal));
  end loop;
  close c1;
end;


declare
 
       my_sal   emp.sal%TYPE;

       my_job   emp.job%TYPE;  
       factor    integer:=2;
       cursor  c1  is
    
       select  factor *sal     from  emp
 
       where  job=my_job;
   
begin
 
        …
       
        my_job :=’MANAGER’;
       
        open  c1;
         
        loop  
      
        fetch  c1  into  my_sal;
        
        exit   when  c1%NOTFOUND;
 
          …
       
        factor:=factor+1; 
        end loop;
             
        close  c1;
  
end;


游标的属性:

每一个显示定义的游标有四种属性

%NOTFOUND  ,  %FOUND
 
%ROWCOUNT,  %ISOPEN

利用游标属性可存取有关多行查询执行的信

注意仅在过程性语句中可使用游标属性息

而在SQL语句中不能使用

(1) %NOTFOUND 属性

布尔型属性,若最后FETCH语句执行,没有返回行,则值为
 TRUE

(2) %FOUND 属性


布尔型属性,如果最后一个FETCH语句执行时返回行,
 %FOUND为TRUE

在第一个FETCH语句执行前,%FOUND计算得到为NULL值

(3) %ISOPEN属性


布尔型属性,当一个游标是打开时,该属性值为TRUE,否则
 为FALSE

对游标工作区检索记录必须先打开游标,否则导致系统错
 误。可用该属性判断游标是否已打开

(4) %ROWCOUNT


数字型属性,返回当前已从游标工作区中读取的记录数

该属性返回游标打开后,至今由FETCH语句已获取的行数

在刚打开游标时,该属性值为0,所以第一个FETCH语句执行之前,%ROWCOUND返回 0。

ey:

loop
 fetch c1 into my_ename,my_deptno;
 if c1%rowcount>10 then
   ...
 end if;
end loop;

在一个PL/SQL块中,可打开多个游标

1: 在数据库表data_table中,存放有实验数据,现对实验的数据进行处理, 将计算结果存入库表temp中。

declare

       num1   data_table.n1%type;

       num2   date_table.n2%type;

       num3   data_table.n3%type;

       result   temp.col1%type;

       cursor  c1  is
 
         select  n1,n2,n3   from  data_table
 
         where  exper_num=1;
begin

       open  c1;
 
      loop 
 
         fetch  c1  into  num1,num2,num3;
 
        exit  when  c1%NOTFOUND;

         result:=num2/(num1+num3);

         insert  into  temp   values (result,null,null);
 
        end loop;
  
      close  c1;

        commit;
 
end; 

2: 查询10号部门雇员工资,当查询到第一个工资大于$2000的
  雇员时停止,并按工资少于$2000的雇员人数在TMP表中生成 相同个数的雇员号 


DECLARE
 v_deptno emp.deptno%type:=10;
 v_sal emp.sal%type;
 cursor c1 is 
  select sal from emp
  where deptno=v_deptno;
  order by sal ASC;
BEGIN
  open c1;
  loop
    fetch c1 into v_sal
    exit when v_sal>=2000;
  end loop;
  for i in 1..c1%rowcount loop
     insert into tmp(deptno,empnp)
     values(10,8000+i*100);
  end loop;
  close c1;
  commit work;
END;

3: 查询10号部门所有雇员姓名、工资,并插入到一个临时表 
 TMP 中

DECLARE 
  v_deptno emp.deptno%type:=10;
  cursor c1 is 
    select ename,sal from emp
    where deptno=v_deptno;
  emp_rec c1%rowtype;
BEGIN
  open c1;
  fetch c1 into emp_rec;
  while c1%found loop
     insert into tmp(ename,sal)
     values(emp_rec.ename,emp_rec.sal);
     fetch c1 into emp_rec;
  end loop;
  close c1;
  commit work;
END;

sql>select * from tmp;

隐式光标

ORACLE在处理每一个不与显示说明光标相关的SQL  
  语句时,隐式地打开一光标

PL/SQL可以以“SQL“引用最近的隐式光标

在程序中不能用OPEN,FETCH,CLOSE控制隐式光标

但可利用光标属性存取最近执行的SQL语句的有关信息

SQL光标与显示光标有相同的四种属性

SQL光标属性值总是涉及最后执行的SQL语句

这些语句
包括有insert, update, delete和 select  into语句

隐式光标四种属性类似显示光标属性, 但不同之处是:

%ROWCOUNT属性返回由 insert, update或 delete所影响的行数以及由select  into所选择的行数。

注意:

select  into返回的行数不能多于一行,如果多于一行,将引
起预定义例外too_many_rows。

%ISOPEN属性:

oracle在执行每一个相关的SQL语句后, 自动地关闭SQL
光标, 所以SQL光标的%isopen 属性总是FALSE。

三、游标中FOR循环的使用

当使用游标FOR循环时,系统隐式地说明它的循环控制
变量为一个记录变量,

执行FOR语句时系统自动打开游标,重复地从游标工作区中读取记录行放置到记录变量的字
 段中

在所有记录行处理完成或被中断退出循环时,自动关闭游标

游标FOR循环语法形式:

 FOR    循环计数器    IN    游标名  LOOP

   .........
 END LOOP;



declare
 
          result  temp.col1%type;

          cursor  c1  is
 
           select  n1,n2,n3
 
           from  data_table
 
           where  exper_num=1;
      
begin

          for  c1rec   IN  C1  loop
  
            result:=c1rec.n2/(c1rec.n1+c1rec.n3);

              insert  into  temp
 
             values  (result,null,null);
  
           end loop;

           commit;
      
end;


其中,c1rec为系统隐式说明为一记录型变量

等价于  c1rec    c1%ROWTYPE


它的各字段可存储由游标c1所获取的全部列值

该记录变量
 仅定义在循环内部,循环体中的语句序列是对满足游标查询的
 每一行执行一次

declare
  v_dno dept.deptno%type:=10;
  cursor c1 is
    select ename,sal from emp
    where deptno=v_dno;
  emp_rec c1%rowtype;
begin
  for emp_rec in c1 loop;
  insert into tmp(ename,sal)
  values(emp_rec.ename,emp_rec.sal);
  end loop;
  commit work;
end;

declare
  cursor c1 is 
  select * from emp
  where rownum<=10
  order by ename;
begin
  for crec in c1 loop
    dbms_output.put_line(crec.ename);
  end loop;
end;

若不用游标FOR循环,PL/SQL代码
       
declare
         
 cursor  c1  is
 select  *   from  emp

 where   ROWNUM<=10
         
   order  by  ename;
          
  v_rec  emp%rowtype;
      
begin
     
     open  c1;
 
     fetch  c1  into  v_rec;

     while   c1%found  loop
  
        dbms_output.put_line(
to_ char(c1%rowcount)||‘ ’||v_rec.ename);

        fetch  c1  into v_rec.ename;
     end loop;

     close c1;
    
end;     

若用带参数的游标,PL/SQL程序块代码
        

declare
 
  cursor  c1(pattern  varchar2) is
 
  select  *  from  emp
 
  where   ename  like  pattern ||’%’ and  rownunt<=10

  order  by  ename;
      
begin
 
   for  crec  in  c1(‘MAR’) loop
   dbms_output.put_line (crec.ename);
   end loop;
     
end; 


游标使用户可以在PL/SQL程序块中先执行一个查询,然后检索查询所得的记录

在程序块的说明部分定义游标并提供要执行的查询

在程序块的执行部分,需先打开游标,接着读取记录,最后关闭游标

用游标FOR循环处理一个游标工作区记录时,可以把打开游标的过程和读取记录的过程合并在一起

带参数游标提供了更大的灵活性并提高了游标在类似查 询中的重用率

当一个查询返回多条记录时必需使用显示游标

只要有可能,请在代码中使用带参数的游标,这样可重用这些游标,会降低必要的开支量。

四.游标的where current of子句

如果需要对游标选择的行或者列进行更新或者删除,则游标定义语句中必须使用for update 选项


其作用为:

迫使Oracle锁定游标结果集的行,防止其他事务处理更新或 删除相同的行,直到当前事务提交或回滚为止

语法格式:

Select ….from ….for update [of column[,column]…..][nowait]

如果在游标中使用了for update 子句,则在delete 和update 语句中可以使用where current of<cursor_name>子句
以修改或删除游标结果集当前行所对应的表中数据。


带有 for update 子句的游标

declare
      cursor sal_cursor is 
      select sal from emp where deptno=30
       for update of sal nowait;
begin
      for emp_record in sal_cursor loop
       update emp set sal=emp_record.sal*1.1
       where current of sal_cursor;
      end loop;
end;

五.游标变量(REF CURSOR)


1.游标变量是动态的,它不与任何特定的查询绑定在一起。

2.可以为任意兼容的查询打开游标变量,从而提高更好的灵活性。

创建游标变量(REF CURSOR)的步骤

1.定义Ref cursor类型,即引用游标类型
2.声明这种游标类型的变量。

强类型游标   (有返回值类型)
TYPE DEPT_CUR IS REF CURSOR
RUTURN dept%ROWTYPE;
cur1 DEPT_CUR;

open cur1 for 
select * from dept where deptno=20;

弱类型游标   (没有返回类型)
TYPE MYCUR IS REF CURSOR;

游标和游标变量的异同:

游标是数据库中一个命名的工作区,当游标被声明后,他就与一个固定的SQL想关联,

在编译时刻是已知的,是静态的.它永远指向一个相同的查询工作区.

游标变量可以在运行时刻与不同的SQL语句关联,在运行时可以取不同的SQL语句.

它可以引用不同的工作区.

  

原文地址:https://www.cnblogs.com/wust221/p/3069117.html