PL/SQL笔记--PLSQL应用开发_游标

来源:互联网 发布:摄影杂志 知乎 编辑:程序博客网 时间:2024/06/11 05:50

1、CURSOR综述

 cursor是用于提取多行数据集

 游标的使用

 

2、申明游标

 申明语法

declare

       CURSER 游标名 IS 查询语句

注:如果在查询语句中使用了pl/sql变量,变量声明必须放在游标的前面

 

DECLARE

       v_major  studengs.major%type;

       CURSOR  c_student  IS

              SELECT  first_name,last_name

              FROM  students

              WHERE  major = v_major

 

 变量名与列名类型要一致

 游标必须在PL/SQL快的声明部分定义

 定义游标时并没有生成数据,只是将定义信息保存到数据字典中;

 游标定义后,可以使用 游标名%ROWTYPE定义游标类型变量

3、打开游标  OPEN 游标名

PS:当打开游标的时候,就代表所定义的游标后面的查询语句真正的被执行,在打开游标的的时候变量一定要去初始化值,否则这个值就是一个空值,导致查询后面的where条件就失去了意义。最后把执行的查询结果返回到游标所定义的空间当中,并且在游标当中会提供一些操作方法来操作这些语句

注:如果游标对应的select语句使用了“FOR UPDATE”(行级排他锁)选项,则此游标锁定

说明:

 检查变量的值

 执行游标定义时对应的select语句将查询结果检索到工作区中

 游标指针指向第一个元素

 一旦游标打开,就无法再次打开,除非先关闭

 如果游标定义中的变量值发生变化,则只能在下次打开游标时才起作用

4、检索游标  FETCH  FROM  CURSOR

 从游标中提取数据,fetch的两种形式

Ø  fetch  游标名  INTO 变量1,变量2,……;

Ø  fetch  游标名  INTO  Record_var 或者是%ROWTYPE类型变量

Ø  注:每fetch一次,只能从游标中提取一行

 说明

Ø  在使用fetch语句之前必须先打开游标

Ø  对游标第一次使用fetch时,游标指针指向第一条记录,因此操作的对象是第一条记录,使用后,游标指向下一条记录

Ø  游标指针只能向下移动,不能回退

Ø  INTO子句中的变量个数,顺序,数据类型必须与游标工作区每行记录的字段数,顺序以及数据类型一一对应

5、关闭游标   close 游标名

 游标使用后应该关闭【务必谨记

fetch 关闭后的游标 是非法的

关闭一个关闭了的游标也是非法的

 说明:

游标所对应的内存工作区变为无效,释放与游标相关的系统资源

 

declare
       v_id  number(4);
       CURSOR  my_cur  IS --声明游标
               select  first_name,salary,start_date  from  s_emp  where  id = v_id;
       v_fn  s_emp.first_name%type    --定义三个变量用以接收游标里面查询出来的参数
       v_sal  s_emp.salary%type;
       v_sd  s_emp.start_date%type;
begin
     v_id := &id--根据输入的id号码来查询出所对应的结果值
     open  my_cur;    --打开游标
     fetch  my_cur  into  v_fn,v_sal,v_sd;   --检索游标
     --打印输出结果
     dbms_output.put_line('id:' || v_id || '  name:' || v_fn || ' salary:' || v_sal || ' start_date:' || v_sd );
     close my_cur;       --关闭游标(必须的)
end;

--查询到的结果是这样的:id:2  name:LaDoris salary:1600 start_date:08-5月 -90

 

6、游标的属性

 %FOUND 如果fetch到数据。则返回true,否则返回false

 %NOT FOUND  如果fetch不到数据,则返回true,否则返回false

 %ISOPEN  如果当前游标已打开,则返回true

 %ROWCOUNT 返回当前游标的指针位移量,当然,游标一定要处于打开状态

7、游标的fetch循环

 采用LOOP循环

 

LOOP

       FETCH  游标名  INTO ……  --提取数据

       EXIT  WHEN 游标名%NOTFOUND;--退出条件

END  LOOP --结束循环

close 游标名 --关闭游标

 

 采用while循环

 

while 游标名%FOUND  LOOP

       fetch  游标名  INTO ……

END  LOOP

close 游标名 –关闭游标

--注意:在使用while的时候,在while之前一定要先fetch一下先,否则进不了循环体,因为开始是空的

 

 采用FOR循环

 

FOR  var  IN  cursor  LOOP

……

END LOOP

注:在for循环中,会自动打开,自动fetch,和自动关闭游标,所以,使用for循环是最为简便的方法

 

8、一个游标的具体实例:

       利用LOOP或WHILE循环和游标来输出工资小于1500远的员工全名、职称,入职时间

方法一:分别定义变量和游标

 

declare
       v_fName varchar2(128);        --定义三个变量来存放数据
       v_title s_emp.title%type;
       v_startDate s_emp.start_date%type;
       CURSOR  my_cur  IS        --声明一个游标
       select  first_name|| ' '||last_name,title,start_date  from  s_emp  where  salary<1500;
begin
     open  my_cur;
     loop
       fetch  my_cur  into  v_fName,v_title,v_startDate;
       dbms_output.put_line(v_fName|| ', ' || v_title ||', ' || v_startDate );  --输出结果
       exit when my_cur%notfound --跳出循环的条件
     end loop;
     close my_cur;       --关闭
end;

 

方法二:改写方法一

下面是这个例子的第二中写法:不需要很麻烦的定义三个变量来存放数据,可以直接定义一个游标型的变量来存放游标里面存放的所有的数据

 

declare
       --v_fName varchar2(128);
       --v_title s_emp.title%type;
       --v_startDate s_emp.start_date%type;
       CURSOR  my_cur  IS
       select  first_name|| ' '||last_name n,title,start_date sd  from  s_emp  where  salary<1500;

              --为了方便起见,我们给上面查询到的字段起了一个别名
       emp  my_cur%rowtype;--直接定义一个游标类型一样的变量,用来存放所有的数据
begin
     open  my_cur;
     loop
       fetch  my_cur  into  emp;--直接把数据给扔到上面定义的变量里面
       dbms_output.put_line(emp.n|| ', ' || emp.title ||', ' || emp.sd );
       exit when my_cur%notfound;
     end loop;
     close my_cur;
end;

 

方法三:直接使用for循环

 

其实上面括号里面的红色部分就是一个游标,由此可以看出,当使用for循环的时候,是最简便的方法

在for循环中,变量因子无需声明,游标也无需显式的打开和关闭,并且也不需要显式的去fetch数据。

当然红色的部分也可以在DECLARE里面事先定义好

9、for循环检索游标的特性

 系统隐含的定义了一个数据类型为%ROWTYPE的变量,并以此做为循环的计算器

 系统自动打开游标,不用显式的使用OPEN语句打开

 系统重复的自动从游标工作区中提取数据并放入计数器变量中

 系统自动进行%FOUND属性检查以确定是否有数据

 当游标工作区中所有的记录都被提取完毕或循环中断时,系统自动的关闭游标。

10、带参数的游标

 

DECLARE

       cursor c_student(p_major  students.major%typeIS

       select * from student where najor = p_major;

BEGIN

       OPEN  c_student(101); --传入参数

       ……

 

注意:

 定义参数化游标时,只能指定参数的类型,而不能指定参数的长度、精度、刻度,比如VARCHAR2(20),只能是VARCHAR2,后面的不能再有括号里面的数据

 打开带参数的游标实参的个数和数据类型等必须与游标定义时形参个数和数据类型等相匹配。

11、利用游标更新或删除数据

 游标定义语法

cursor 游标名 IS

SELECT  select语句  FROM 表 FOR UPDATE

【OF 列名】【NOWAIT】

 注意

Ø  打开游标时对相应的表加锁(通常select操作不会在数据上设置任何锁),其他用户不能对该表进行DML操作

Ø  若数据对象已经被其他会话加锁,则当前会话挂起等待(默认状态),若指定了NOWAIT语句,则不会等待,返回一个Oracle错误

Ø  对于多表查询时,可以通过of子句指定某个要加锁的列表的形式,对特定的表进行加锁,而其他的表不加锁;否则的话所有的表都会加锁,别人就会无法区操作

Ø  当用户执行COMMIT或ROLLBACK操作时,数据上的锁才会被自动的释放

 其操作的语法

更新或是修改的语法为

       UPDATE | DELETE……

where current of 游标名

注意:

              如果游标定义时没有使用for UPDATE子句,则不能利用该游标修改或删除数据库中的数据

12、案例

修改员工的工资,如果员工的部门号为10,则工资提高100;如果部门号为20,则工资提高150;如果部门号为30,则工资提高200;否则工资提高250.

 

declare
       cursor c_emp is  select  *  from  emp  for update;
       v_increment  number;
begin
     for  v_emp  in  c_emp  loop
          case  v_emp.empno
            when  10  then  v_increment := 100;
            when  20  then  v_increment := 150;
            when  30  then  v_increment := 200;
            else  v_increment := 250;
          end  case;
          update  emp  set  sal=sal + v_increment  where  current  of  c_emp;
     end loop;
     commit             --注意要提交事务
end;

 

13、隐式游标

 概念:

Ø  所有的SQL语句都有一个执行的缓冲区,隐式游标就是指向该缓冲区的指针,由系统隐含的打开、处理和关闭。隐式游标有称作SQL游标

Ø  隐式游标主要用于处理insert、UPDATE、DELETE以及单行的select……INTO语句,没有OPEN、fetch、close等操作的命令

 隐式游标的属性

Ø  SQL%isopen:布尔型值,判断隐式游标是否已经打开。对用户而言,该属性值始终为false,因为操作时系统自动打开,操作完后立即自动关闭

Ø  SQL%FOUND:布尔型值,判断当前的操作是否会对数据库产生影响。如果有数据的插入、删除、修改或查询到数据,则返回true,否则返回false

Ø  SQL%NOTFOUND:布尔型值,和上面是相反的理解

Ø  SQL%rowcount:数值型,返回当前操作所涉及的数据库中的行数。

 下面是一个案例

修改员工号为1000的员工的工资,将他的工资增加100.如果该员工不存在,则向emp表中插入一个员工号为1000,工资为1600的员工

方法一:

 

begin
    update emp set sal=sal+100  where  empno=1000;
    if  sql%NOTFOUND  then
       INSERT  INTO  emp(empno,sal) values(1000,1600);
    end if;
    commit;
end;

 

              方法二:

 

begin
    update emp set sal=sal+100  where  empno=1000;
    if  sql%rowcount = 0  then
       INSERT  INTO  emp(empno,sal) values(1000,1600);
    end if;
    commit;
end;

 

14、游标变量

 概念

游标变量是一个指向多行查询结果集的指针,不与特定的查询绑定,因此具有非常大的灵活性,可以在打开游标变量时定义查询,可以返回不同结构的结果集

 使用游标变量包括

Ø  游标引用类型(ref cursor)

Ø  声明游标变量

Ø  打开游标变量

Ø  检索游标变量

Ø  关闭游标变量

001、REF  CURSOR类型【游标变量类型】

 REF  CURSOR分成如下2种:

Ø  strong cursor 强类型游标,申明此类型游标时必须指定返回类型,如:

TYPE 游标变量名  IS  REF  CURSOR  RETURN  type

type 可以是任务合法的Oracle数据类型

Ø  WEAK cursor 若类型游标,申明此类型游标时无需指定返回类型,如:

TYPE 游标变量名  IS  REF  CURSOR

注:REF  CURSOR声明时也是放在DECLARE快中

002、一个强类型的例子

 

declare 
         type
 strong_cursor is  ref  cursor  return  s_emp%rowtype;--这是一个强类型
        
 my_c1  strong_cursor;
         emp 
 s_emp%rowtype;  --由于这是一个强类型,所以只能是跟游标类型相对应的变量
         begin
              open 
 my_c1  for  select * from  s_emp  where dept_id=41;
             
 dbms_output.put_line('----------输出41部门的员工----------------');
              loop
                  fetch  my_c1  into 
 emp;
                  exit  when 
 my_c1%notfound;
                
  dbms_output.put_line(emp.id||''||emp.salary);
              end  loop;
              close  my_c1;
             
              open 
 my_c1  for  select * from  s_emp  where  salary>1000;
          
    dbms_output.put_line('----------输出工资大于1000的员工 -------------');
              loop
                  fetch 
 my_c1  into  emp;
                  exit  when
  my_c1%notfound;
                
  dbms_output.put_line(emp.id||''||emp.salary);
              end  loop;
        end;

 

由上面的例子可以看出,使用游标变量使得查询等变的很随意

003、一个若类型的例子

 

004、sys_refcursor类型

 系统自带一个类型:sys_refcursor

 此类型可以用来定义所有:若类型

就像下面一个例子

 

declare
     type id_list is  table  of  integer INDEX BY binary_integer;
     type  name_list  is  table  of  varchar2(200) INDEX BY binary_integer;
     ids  id_list;
     names  name_list;
     my_cur  sys_refcursor;
begin
     open my_cur  for  select  id, first_name from  s_emp;
     fetch  my_cur  bulk  collect  INTO ids, names;
     close my_cur;
     for i in  ids.first..ids.last  loop
         dbms_output.put_line('ID = '||ids(i)||'NAME = '||names(i));
     end  loop;
end;


原创粉丝点击