PL/SQL之--游标

一、游标简介

  在PL/SQL中执行SELECT、INSERT、DELETE和UPDATE语句时,ORACLE会在内存中为其分配上下文区(Context Area),也称为缓冲区。游标是指向该区的一个指针。它提供了一种在多行结果集中对每一行数据分别进行单独处理的方法。用户可以用SQL语句逐一从游标中获取记录,并赋给主变量,交由主语言进一步处理。

  oracle中游标有如下两种:

  • 静态游标:分为显式游标和隐式游标。
  • ref游标:引用类型,类似于C中的指针。

二、静态游标

   静态游标分为显式游标和隐式游标,静态游标在编译的时候已经确定,然后把结果复制到内存中。

  1、隐式游标

    在PL/SQL中执行数据操作语句查询(查询一行),修改,删除(DML语句)时,oracle预先定义一个名称为SQL的隐式游标,在执行DML语句之后通过检查隐式游标的属性获取与最近执行的SQL语句相关信息。用户只能通过隐式游标的相关属性,从而完成相应的操作。在隐式游标的工作区中,所存放的数据是与用户自定义的显示游标无关的、最新处理的一条SQL 语句所包含的数据。

调用语法为:SQL%

  隐式游标属性%found、%notfound、%rowcount、%isopen这四种,详情如下:

  • %found: 只有DML语句影响一行或多行时,%found属性才返回true;
  • %notfound: 如果DML语句没有影响任何行数,则%notfound返回true;
  • %rowcount: 返回DML影响的行数,如果DML没有影响任何行数,则%rowcount返回0;
  • %isopen: 判断SQL游标是否已经打开,在执行SQL语句之后,oracle会自动关闭SQL游标,所有隐式游标的%isopen属性始终为false;

  当PL/SQL抛出异常的时候,将不使用属性%found、%notfound、%rowcount来查明DML语句是否已经影响了行数。

  代码示例如下:

  select中使用隐式游标

-- select隐式游标
declare
  v_id number := 1;
   v_username varchar2(255);
   v_age number;
   v_password varchar2(255);
begin
  select username, age, password into v_username, v_age, v_password from person where id = v_id;
   dbms_output.put_line(SQL%ROWCOUNT);--影响的行数
  if SQL%ISOPEN then
      dbms_output.put_line(open);    
  else    
    dbms_output.put_line(not open);
  end if;   
 
  if SQL%FOUND then --成功
    dbms_output.put_line(found);
  else --失败
    dbms_output.put_line(not found);  
  end if; 
  
  if SQL%NOTFOUND then --失败
    dbms_output.put_line(not found);
  else --成功
    dbms_output.put_line(found);  
  end if;    
end;

  insert中的隐式游标

-- insert 隐式游标
declare
   v_id number := 26;
   v_username varchar2(255) := xiaoming;
   v_age number := 19;
   v_password varchar2(255) := xiao123 ;
begin
  insert into person (id, username, age, password) values (v_id, v_username, v_age, v_password);
  -- commit; --此时commit后面都会没有值
  dbms_output.put_line(SQL%ROWCOUNT);--增加的行数
  if SQL%ISOPEN then
      dbms_output.put_line(open);    
  else    
    dbms_output.put_line(not open);
  end if;   
 
  if SQL%FOUND then --成功
    dbms_output.put_line(found);
  else --失败
    dbms_output.put_line(not found);  
  end if; 
  
  if SQL%NOTFOUND then --失败
    dbms_output.put_line(not found);
  else --成功
    dbms_output.put_line(found);  
  end if;    
  -- commit;
end;

  update中使用隐式游标

-- update 隐式游标
declare
 v_id number := 25;
 v_age number := 100; 
begin 
  update person set age = v_age where id = v_id;
  --commit; --此时commit后面都会没有值
  dbms_output.put_line(SQL%ROWCOUNT);--修改的行数
  if SQL%ISOPEN then
      dbms_output.put_line(open);    
  else    
    dbms_output.put_line(not open);
  end if;    
  
  if SQL%FOUND then --成功
    dbms_output.put_line(found);
  else --失败
    dbms_output.put_line(not found);  
  end if; 
  
  if SQL%NOTFOUND then --失败
    dbms_output.put_line(not found);
  else --成功
    dbms_output.put_line(found);  
  end if;       
  commit;
end;

  delete中使用隐式游标

--  delete 隐式游标
declare
   v_id number := 26; 
begin
  delete from person where id = v_id;
  -- commit; --此时commit后面都会没有值
  dbms_output.put_line(SQL%ROWCOUNT);--删除的行数  
  if SQL%ISOPEN then
      dbms_output.put_line(open);    
  else    
    dbms_output.put_line(not open);
  end if;   
  
  if SQL%FOUND then --成功
    dbms_output.put_line(found);
  else --失败
    dbms_output.put_line(not found);  
  end if; 
  
  if SQL%NOTFOUND then --失败
    dbms_output.put_line(not found);
  else --成功
    dbms_output.put_line(found);  
  end if;    
  commit;
end;

  2、显式游标  

  显示游标时由用户显示声明的游标。根据在游标中定义的查询,查询返回的行集合可以包括0行货多行,这些行称为活动集。游标将指向活动集中的当前行。
显示游标的操作过程如下:
  1)、定义游标

    CURSOR 游标名称 is 查询语句;
    CURSOR 游标名称[参数1,参数2、...]
    [RETURN 数据类型]
    IS
    查询语句;
    游标参数只能为输入参数,格式为:
    参数名称 [IN] 数据类型 [值]

  2)、打开游标

    open 游标名称 [参数名称 => 值, 参数名称 => 值,..];
  3)、从游标中获取结果集

    FETCH 游标名称 INTO 变量;

    执行FETCH语句时,每次返回一条数据行,并自动将游标移动指向下一条数据行。当检索到最后一条数据时,如果再次执行FETCH语句,操作将失败,游标属性%NOTFOUND设置为TRUE。所以每次执行完FETCH语句后,检查游标属性%NOTFOUND就从而判断FETCH语句是否执行成功并返回一个数据行,确定是否为最后一行。

  4)、关闭游标

    CLOSE 游标名称;

  显式游标属性如下:

  • %FOUND 布尔型属性,当最近一次提取游标操作FETCH成功则为 TRUE,否则为FALSE;
  • %NOTFOUND 布尔型属性,与%FOUND相反;
  • %ISOPEN 布尔型属性,当游标已打开时返回 TRUE;
  • %ROWCOUNT 数字型属性,返回已从游标中读取的记录数;

   代码示例如下:

  不带参数不带返回值的游标

-- 不带参数的游标不带返回值的游标    
declare
   -- 1、定义一个游标,将person所有数据提取出来
   cursor c_person is
   select * from person;
   r_person person%rowtype;
begin
  --2、打开游标
  open c_person;
  --3、提取数据
  loop
    fetch c_person 
    into r_person;
    exit when c_person%notfound; -- 下面没有数据的时候,退出
    dbms_output.put_line(id:||r_person.id);
    dbms_output.put_line(username:||r_person.username);
    dbms_output.put_line(age:||r_person.age); 
  
  end loop;
  --4、关闭游标
  close c_person;
end;

  带参数不带返回值的游标

--带参数不带返回值的游标
declare
   -- 1、定义一个游标,将person所有数据提取出来
   cursor c_person(v_age number) is
   select * from person where age < v_age;
   r_person person%rowtype;
begin
  --2、打开游标
  open c_person(20);--打开游标,传递参数值
  --3、提取数据
  loop
    fetch c_person 
    into r_person;
    exit when c_person%notfound; -- 下面没有数据的时候,退出
    dbms_output.put_line(id:||r_person.id);
    dbms_output.put_line(username:||r_person.username);
    dbms_output.put_line(age:||r_person.age); 
  
  end loop;
  --4、关闭游标
  close c_person;
end;

  带参数带返回值的游标,返回type类型

-- 带参数带返回值的游标,返回type类型
declare
    r_person person%rowtype;
   
   -- 1、定义一个游标,有参数和返回值
   cursor c_person(v_age number) 
     return  person%rowtype
   is
      select * from person where age < v_age;
begin
  --2、打开游标
  open c_person(20);--打开游标,传递参数值
  --3、提取数据
  loop
    fetch c_person 
    into r_person;
    exit when c_person%notfound; -- 下面没有数据的时候,退出
    dbms_output.put_line(id:||r_person.id);    
    dbms_output.put_line(username:||r_person.username);
    dbms_output.put_line(age:||r_person.age); 
    dbms_output.put_line(password:||r_person.password);
    
  end loop;
  --4、关闭游标
  close c_person;
end;

  带参数带返回值的游标,返回自定义类型

 -- 带参数带返回值的游标,返回自定义类型
declare
   type person_record_type is record(
        t_username person.username%type,
        t_age person.age%type,
        t_password person.password%type
   );   
   v_person_record person_record_type;
   
   -- 1、定义一个游标,有参数和返回值
   cursor c_person(v_age number) 
     return person_record_type
   is
      select username, age, password from person where age < v_age;
begin
  --2、打开游标
  open c_person(20);--打开游标,传递参数值
  --3、提取数据
  loop
    fetch c_person 
    into v_person_record;
    exit when c_person%notfound; -- 下面没有数据的时候,退出
    dbms_output.put_line(username:||v_person_record.t_username);
    dbms_output.put_line(age:||v_person_record.t_age); 
    dbms_output.put_line(password:||v_person_record.t_password);
    
  end loop;
  --4、关闭游标
  close c_person;
end;

 三、ref 类型游标

  ref游标也是一个指向多行查询结果集合中当前数据行的指针。但与游标不同的是,ref游标是动态的,而游标是静态的。隐式游标和显示游标都是静态定义的。它们在编译的时候结果集就已经被确定。而ref游标在运行时候可以指向不同查询语句的结果集。   定义游标变量类型,可以采用强类型定义和弱类型定义两种。强类型定义必须指定游标变量的返回值类型,而弱类型定义则定义说明返回值类型。

  语法如下:

  1、定义一个REF CURSOU 数据类型
  type 游标名称 IS REF CURSOR
  return 返回类型;
  2、声明一个该数据类型的游标变量,如:
  游标变量名称 游标名称;

  示例代码如下:

  ref 类型游标弱类型(无返回值)     

-- ref 类型游标弱类型(无返回值)
declare
   type c_person_type is ref cursor;
   c_person c_person_type;
   r_person person%rowtype;
begin
   -- 遍历person表
   open c_person for select * from person_back;--person_back与person表结构一样
   loop
    fetch c_person 
    into r_person;
    exit when c_person%notfound; -- 下面没有数据的时候,退出
    dbms_output.put_line(id:||r_person.id);
    dbms_output.put_line(username:||r_person.username);
    dbms_output.put_line(age:||r_person.age); 
  end loop;
  --关闭游标
    dbms_output.put_line(---------------------------------);   
  -- 变量person_back
   open c_person for select * from person;
   loop
    fetch c_person 
    into r_person;
    exit when c_person%notfound; -- 下面没有数据的时候,退出
    dbms_output.put_line(id:||r_person.id);
    dbms_output.put_line(username:||r_person.username);
    dbms_output.put_line(age:||r_person.age); 
  end loop;
  close c_person;
end;

  ref 类型游标强类型(返回row type)

-- ref 类型游标强类型(返回row type)
declare
  type c_persontype is ref cursor return person%rowtype;
  c_person c_persontype;
  r_person person%rowtype;
begin
  -- 遍历person表
  open c_person for select * from person;
  loop
  fetch c_person
  into r_person;
   exit when c_person%notfound; -- 下面没有数据的时候,退出
    dbms_output.put_line(id:||r_person.id);
    dbms_output.put_line(username:||r_person.username);
    dbms_output.put_line(age:||r_person.age); 
  end loop;
  close c_person;
  dbms_output.put_line(--------------------------------------);
   -- 遍历person_back表
  open c_person for select * from person_back;
  loop
  fetch c_person
  into r_person;
   exit when c_person%notfound; -- 下面没有数据的时候,退出
    dbms_output.put_line(id:||r_person.id);
    dbms_output.put_line(username:||r_person.username);
    dbms_output.put_line(age:||r_person.age); 
  end loop;
  close c_person;
end;  

  -- ref 类型游标强类型(返回自定义类型)

-- ref 类型游标强类型(返回自定义类型)
declare
  -- 定义一个数据类型
  type person_record is record(
       username varchar2(255), -- 定义好长度,否则会保持
       age number,
       password varchar2(255)
  );
  -- 定义返回自定义数据类型的游标类型
  type c_persontype is ref cursor return person_record;
  c_person c_persontype; -- 游标类型的变量
  r_person person_record; -- 自定义数据类型变量
begin
  -- 遍历person表
  open c_person for select username, age, password from person;
  loop
  fetch c_person
  into r_person;
   exit when c_person%notfound; -- 下面没有数据的时候,退出
    dbms_output.put_line(username:||r_person.username);
    dbms_output.put_line(age:||r_person.age);
    dbms_output.put_line(password:||r_person.password); 
  end loop;
  close c_person;
  dbms_output.put_line(--------------------------------------);
   -- 遍历person_back表
  open c_person for select username, age, password from person_back;
  loop
  fetch c_person
  into r_person;
   exit when c_person%notfound; -- 下面没有数据的时候,退出
    dbms_output.put_line(username:||r_person.username);
    dbms_output.put_line(age:||r_person.age); 
    dbms_output.put_line(password:||r_person.password);
  end loop;
  close c_person;
end;  

  sys_refcursor

  sys_refcursor是oracle9i以后系统定义的一个refcursor,主要用在过程中返回结果集。我们也可以直接使用sys_refcursor遍历结果集。

  示例代码如下:

declare 
   c_person sys_refcursor;
   r_person person%rowtype;
begin
   -- 遍历person表
   open c_person for select * from person_back;--person_back与person表结构一样
   loop
    fetch c_person 
    into r_person;
    exit when c_person%notfound; -- 下面没有数据的时候,退出
    dbms_output.put_line(id:||r_person.id);
    dbms_output.put_line(username:||r_person.username);
    dbms_output.put_line(age:||r_person.age); 
  end loop;
  --关闭游标
    dbms_output.put_line(---------------------------------);   
  -- 变量person_back
   open c_person for select * from person;
   loop
    fetch c_person 
    into r_person;
    exit when c_person%notfound; -- 下面没有数据的时候,退出
    dbms_output.put_line(id:||r_person.id);
    dbms_output.put_line(username:||r_person.username);
    dbms_output.put_line(age:||r_person.age); 
  end loop;
  close c_person;
end;

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。