数据提取 -- 游标

游标只是一个指向某个结果集的指针.

声明游标: cursor employee_cur IS select * from employees;

打开游标: open employee_cur;

提取数据: fetch employee_cur into employee_rec;  -- employee_rec 是employees%rowtype类型

关闭游标: close employee_cur;

也可以多表 join 作为结果集.

declare

cursor joke_feedback_cur

is

select j.name, r.laugh_volume, c.name

from joke J, response R, comedian C

where j.joke_id = r.joke.id

and r.joker_id = c.joker_id;

begin

...

end;

不管是哪一种游标类型, pl/sql 执行一个 sql 语句使用的都是相同的操作方式. 有时, plsql 会替我们执行这些步骤

执行步骤

游标的属性

-- 隐式游标举例 --
Function book_title (isbn_in IN books.isbn%TYPE)
return books.title%type
IS
    return_value    book.title%type;
BEGIN
    SELECT     title      -- 隐式游标
    into    return_value
    from    books
   where    isbn = isbn_in;
  
    return    return_value;
   
    exception    -- 隐式游标的异常处理
        when no_date_found
        then
            return null;
        when too_many_rows  -- 隐式游标异常
        then
            errpkg.record_and_stop ('Data integrity error for:' || isbn_in);
            raise;       
END;

隐式游标有可能自动抛出 2 种异常

  • 查询没有找到能够匹配我们条件的任何一行. 这种情况下, 数据库会抛出异常 NO_DATA_FOUND
  • select 语句返回的结果已经超过了一行. 这种情况下, 数据库会抛出 TOO_MANY_ROWS 异常.

隐式游标属性

   1:  -- chap15_01.sql
   2:  create or replace procedure remove_from_circulation( isbn_in in books.isbn%type)
   3:  is
   4:  begin
   5:      delete from book where isbn = isbn_in;
   6:  end;
   7:  /
   8:  show errors;
   9:   
  10:  -- chap15_02.sql
  11:  create or replace procedure show_boo_count 
  12:  is
  13:      l_count    integer;
  14:  begin
  15:      select count(*)
  16:        into l_count
  17:        from books;
  18:        
  19:        remove_from_circulation('0-000-0000-0');
  20:        dbms_output.put_line(sql%rowcount);
  21:  end;

上例中, 无论book表中有多少条记录, 我从输出窗口看到的总是”0”, 这是因为我在SELECT INTO 语句之后又调用了 remove_from_circulation, 因此 SQL%ROWCOUNT 反映的是这个愚蠢的, 不可能有结果的 delete 语句的属性, 而不是查询语句的结果.

要想确保检查的确实是正确的SQL语句的属性值, 我们应该在该SQL语句执行之后立即把属性值保存到局部变量中, 否则SQL%ROWCOUNT等属性值, 永远保存的是最后执行的隐性游标的属性.

   1:  -- chap15_03.sql 正确方法
   2:  create or replace procedure show_boo_count 
   3:  is
   4:      l_count    integer;
   5:      l_numfound pls_integer;
   6:  begin
   7:      select count(*)
   8:        into l_count
   9:        from books;
  10:        l_num_found := SQL%ROWCOUNT;  -- 将游标属性保存在局部变量
  11:        remove_from_circulation('0-000-0000-0');
  12:        dbms_output.put_line(sql%rowcount);
  13:  end;

-- 显示游标举例 --
Function jealousy_level (

NAME_IN    IN    friends.NAME%TYPE)    RETURN    NUMBER

AS

cursor jealousy_cur

IS

select location from friends

where name = UPPER(name_in);

jealousy_rec    jealousy_cur%ROWTYPE;    
    retval    NUMBER;

BEGIN

OPEN jealousy_cur;

fetch jealousy_cur into    jealousy_rec;

IF jealousy_cur%found then

if jealousy_rec.location = 'PUERTO RICO' THEN

retval := 10;

elsif jealousy_rec.location = 'CHICAGO' THEN

retval := 1;

end if;

end if;

close jealousy_cur;

return retval;

EXCEPTION

when others then

if jealousy_cur%isopen then

close jealousy_cur;

end if;           
END;

带参数化的游标, 游标的参数只能是 IN 类型的.

使用游标变量的好处是它提供了一种可以在不同的PL/SQL程序间传递查询结构集(从游标获取记录行)的机制.

声明显示游标

1. 不带参数的游标

CURSOR company_cur IS

SELECT company_id FROM company;

2. 带参数的游标

CURSOR name_cur ( company_id_in IN number)

IS

SELECT  name FROM company

WHERE company_id = company_id_in;

3. 带 return 语句的游标

CURSOR emp_cur RETURN employee%ROWTYPE

IS

SELECT * FROM employees

WHERE department_id = 10;

在包中声明游标

   1:  PACKAGE book_info
   2:  IS
   3:    CURSOR titles_cur
   4:    IS 
   5:      select title
   6:        from books;
   7:      
   8:    cursor books_cur(title_filter_in IN books.title%type)
   9:      return books%rowtype
  10:    IS
  11:      select *
  12:        from books
  13:       where title like title_filter_in;
  14:  end;

为什么要把游标定义在包中呢?

答案很简单, 通过在包中定义游标, 我们重用这些查询就更加容易, 也避免了在应用程序中一遍遍的编写相同的数据提取语句.

为什么要使用 return 语句 ?

这个包有点类似 object-c, 实际内容在包体中, 包头只是一个声明, 我们应该尽可能的隐藏包体中的内容, 只给用户看到包头的内容, 那么 return 语句的用处是.

1. 隐藏信息, 隐藏包体信息, 别人只要读包头, 就知道游标的一切, 包括这个游标返回的结果集的类型.

2. 最小化编译, 我们可以随意的修改包体中的 select 语句而不会影响到包头的游标, 这样就可以改进, 重编译代码, 而不用重新编译包头部分, 这也就意味着所有依赖于这个包的程序都不会被置成无效状态, 自然也就不必重新编译.

打开显示游标

OPEN cursor_name[argument, argument …];

也就是说, 当你游标打开时, 如果没有指定 for update 语句, 那么不会为游标的记录集加锁, 这时候, 会保存打开游标时刻的SCN, 如果游标操作过程中, 修改了记录集(游标虚拟表中的记录), 当提交时, 如果SCN 比实际数据块的SCN小, 那么这个游标的修改操作将是不成功的.

从显示游标中提取数据

select 语句构建了一个虚拟数据表, 它的返回集是由 where 语句(或者没有)确定的一系列行, 因此 plsql程序中, 游标就代表着这个虚拟的数据表.

FETCH cursor_name INTO record_or variable_list;

显示游标中的列别名

别名的作用只是为了在结果集中使用方便, 比如你有个计算函数 sum(a, b), 如果不起别名的话, 这列很难引用

   1:  declare
   2:      cursor comp_cur is
   3:          select c.name, sum(inv_amt) total_sales  -- 列别名
   4:            from company C, invoice I
   5:           where C.company_id = I.company_id
   6:             and I.invoice_date BETWEEN '01-jan-2001' AND '31-dec-2001';
   7:      comp_rec comp_cur%rowtype;  -- 这里定义的是结果集的rowtype类型
   8:  begin
   9:      open comp_cur;
  10:      fetch comp_cur into comp_rec;
  11:      ...
  12:  end;
  13:  -- 然后可以在结果集中使用如下:
  14:  IF comp_rec.total_sales > 5000 THEN
  15:      dbms_output.put_line('You have exceeded your credit limit of $5000 by ' ||
  16:                          to_char(comp_rec.total_sales - 5000, '$9999'));
  17:  END IF;
 

关闭显示游标

CLOSE cursor_name;

如果我们在程序中声明并打开了一个游标, 就一定要确保在程序结束时关闭这个游标.

这里要特别注意在包中的游标, 包中的游标, 在异常处理单元都要有关闭游标的确认.

   1:  begin
   2:      open my_package.my_cursor;
   3:      ...
   4:      close my_package.my_cursor;
   5:  exception
   6:      when others then
   7:          IF mypackage.my_cursor%ISOPEN THEN
   8:              CLOSE my_package.my_cursor;
   9:          END IF;
  10:  END;

SELECT … FOR UPDATE

当使用 select 语句从数据库查询记录时, 数据库不会被选择的行添加任何锁. 通常来说, 这是一个非常不错的特性, 因为任何时候被锁定的记录数量都要尽可能的最小, 只有那些已经被修改了但是还没有提交的记录才需要被锁定. 即使这样, 其他人还是可以看到这些记录被修改之前的状态.

不过, 有时候我们希望在程序修改之前就锁住它们. 这时就可以使用 SELECT … FOR UPDATE

   1:  CURSOR toys_cur IS
   2:      SELECT name, manufacturer, preference_level
   3:        from my_sons_collection
   4:       where hours_used = 0
   5:         for update;  -- 没有任何限制
   6:         
   7:  CURSOR fall_jobs_cur IS
   8:      SELECT task, expected_hours, tools_required
   9:        from winterize
  10:       where year_of_task = to_char(sysdate, 'yyyy')
  11:         for update of task;  -- 只针对 task 这列限制

我们也可以夺标SELECT中使用 for update, 这种情况, 只有自己列出的在 for update of 子句中的记录才会被锁定, 换句话说, 必须要有限制.

WHERE CURRENT OF 语句

plsql 为游标的 update 和 delete 提供了 where current of 语句. 这个语句可以让我们很容易的修改最后取出来的数据行.

   1:  update table_name
   2:     set set_clause
   3:   where current of cursor_name;
   4:   
   5:   delete 
   6:     from table_name
   7:    where current of cursor_name;

例如: 删除一个我刚刚取出的数据, 更新一个我刚刚取出的数据.

   1:  declare
   2:      cursor fall_jobs_cur IS select ... same as before ...;
   3:      job_rec fall_jobs_cur%rowtype;
   4:  begin
   5:      open fall_jobs_cur;
   6:      loop
   7:          fetch fall_jobs_cur into job_rec;
   8:          
   9:          exit when fall_jobs_cur%notfound;
  10:          
  11:          if job_rec.do_it_yourself_flag = 'youcandoit' then
  12:              update winterize 
  13:                 set responsible = 'steven'
  14:               where current of fall_jobs_cur;
  15:              commit;
  16:              exit;
  17:          end if;
  18:      end loop;
  19:      close fall_jobs_cur;
  20:  end;

声明 REF CURSOR 类型 游标变量

游标变量是一个指向或者引用底层游标的变量, 是一个指针, 和显示游标不一样, 显示游标已经为结果集的 pl/sql 工作区指定了名字, 而游标变量只是指向这个工作区的引用. 显示游标和隐式游标都绑定到一个专门的查询语句, 从这一点来说, 这两种游标都是静态的, 而游标变量可以用于打开任何一个查询, 甚至在一个程序中执行多个不同的查询.

使用游标变量一个最重要的好处就在于它提供了一种可以在不同的plsql 程序间传递查询的结果集

比如一个session 打开了一个游标, 可以使用游标变量将这个传递给另一个session, 而另一个session 可以关闭这个游标.

创建一个游标变量

TYPE cursor_type_name IS REF CURSOR [RETURN return type];  -- 创建引用游标类型

TYPE company_curtype IS REF CURSOR RETURN company%ROWTYPE;   -- 强类型

TYPE generic_curtype IS REF CURSOR;                          -- 弱类型

从 oracle 9i 开始, 数据库已经替我们定义好了一个弱类型, 直接用就可以了

declare my_cursor SYS_RECURSOR;  -- 定义了真正的游标变量

声明游标变量的方法
cursor_name cursor_type_name;

声明一个游标变量 和 创建一个真正的游标对象, 二者之前的区别很重要, 后者通过游标的sql语句查询出的结果集, 而游标变量指向游标对象. 如图:

打开游标变量

open cursor_name FOR select_statement;

如果是强类型的, 那么类型一定要匹配

从游标变量获取数据, 同静态游标一样

FETCH cursor_variable_name INTO record_name;

FETCH cursor_variable_name INTO varibale_name, variable_name...;

如果是强类型转换, 有可能类型不匹配, 这时候会抛出 rowtype_mistach 异常, 要捕获这个异常

EXCEPTION

WHEN ROWTYPE_MISMATCH THEN

--do something

游标变量的使用规则

记住 游标变量时对一个游标对象或者一个数据库中查询的引用, 而不是游标对象本身.

游标对象的作用范围类似 java 中对象的概念, 堆中的对象, 只要有一个引用(指针) 还在引用这个对象, 这个对象就要保持可以被访问状态.

游标变量不能在包中声明, 因为它没有持久状态.

我们不能通过远程过程调用(RPC) 把游标变量从一个服务器传递给另一个服务器.( 游标变量保存的只是引用)

游标变量作为参数传递

   1:  declare
   2:      type curvar_type is ref cursor return company%rowtype;
   3:      procedure open_query(curvar_out OUT curvar_type)
   4:      is
   5:          local_cur curvar_type;
   6:      begin
   7:          open local_cur for select * from company;
   8:          curvar_out := local_cur;
   9:      end;

游标表达式(用于嵌套游标)

pl/sql programming 15 数据提取的更多相关文章

  1. pl/sql developer导出数据到excel的方法

    http://yedward.net/?id=92 问题说明:使用pl/sql developer导出数据到excel表格中是非常有必要的,一般的可能直接在导出的时候选择csv格式即可,因为该格式可以 ...

  2. pl/sql programming 03 语言基础

    PL/SQL 块结构 最小的有意义的代码单元叫做 块(block). 一个块是一组代码, 这个块给出了执行边界, 也为变量声明和异常处理提供了作用范围, pl/sql 准许我们创建匿名块和命名块, 命 ...

  3. PL/SQL DEVELOPER 导出表数据

    http://jingyan.baidu.com/album/fcb5aff78e6a48edab4a7146.html?picindex=4 1. 导出表数据 打开pl/sql客户端 在左侧 点击t ...

  4. pl/sql对excel数据的导入和导出

    本来这部分是在上篇pl/sql的,但笔者介于此篇的内容,就独立出来了, 1.导出查询结果到excel文件,在查询结果上右键,然后弹出选择框如下: 2.从excel向数据库中导入数据: a.创建要导入的 ...

  5. PL/SQL程序控制结构及在PL/SQL中更改数据和管理事务

    1.条件控制 A. IF条件分支语法: if (条件1) then 语句; elsif (条件2) then 语句; elsif (条件3) then 语句; else 语句; end if; B . ...

  6. PL/SQL Developer插入数据到数据库出现数据中文乱码

    问题描述: 使用PL/SQL Developer往Oracle数据库插入数据,出现中文乱码! 解决办法: 1.执行脚本 select userenv('language') from dual; 结果 ...

  7. pl/sql programming 06 异常处理

    如果 PLSQL发生了错误, 无论是系统错误还是应用错误, 都会抛出一个异常, 当前 PL/SQL 块中执行单元会暂停处理, 如果当前块有一个异常处理单元的话, 控制会转移到当前块的异常处理单元来处理 ...

  8. pl/sql programming 02 创建并运行plsql代码

    /* * chap 02 * ------------------------------------------------- */ create or replace function wordc ...

  9. PL/SQL 查询的数据出现乱码

    解决方法: 1.首先在查询出Oracle数据库的字符集. select userenv('language') from dual; 2.新建系统变量 NLS_LANG,变量值为第一步查询出来的字符集 ...

随机推荐

  1. 关于mysql 连接数

    mysql基本维护,就必须要知道连接数 进入mysql ,show status    Threads_connected  当前的连接数    Connections  试图连接到(不管是否成功)M ...

  2. maven 常用5个命令

    1. mvn help:describe 你是否因为记不清某个插件有哪些goal而痛苦过,你是否因为想不起某个goal有哪些参数而苦恼,那就试试这个命令吧,它会告诉你一切的. 参数: 1. -Dplu ...

  3. BZOJ1821: [JSOI2010]Group 部落划分

    这题乍看很吓人,其实就是一个贪心. 每次取最近的两个点所在的块合并,直到只剩下k块,输出答案. /*************************************************** ...

  4. c# 发送消息到Email

    /// <summary>        /// 发送消息到Email        /// </summary>        /// <param name=&quo ...

  5. LA 3350

    The NASA Space Center, Houston, is less than 200 miles from San Antonio, Texas (the site of the ACM ...

  6. Eclipse Java EE 创建 Dynamic Web Project

    1.      创建一个web工程,此处用eclipse创建(如果对创建web工程很熟悉,可以不看的,本文目的是做一个记录)  1)     打开新建工程对话框,选择Dynamic web Proje ...

  7. MyEclipse — Maven+Spring+Struts+Hibernate 整合 [学习笔记-4]

    引入Hibernate 在pom.xml中加入jar包引用 <!-- hibernate4 --> <dependency> <groupId>org.hibern ...

  8. Project Euler 107:Minimal network 最小网络

    Minimal network The following undirected network consists of seven vertices and twelve edges with a ...

  9. redis资料汇总

    redis资源比较零散,引用nosqlfan上的文章,方便大家需要时翻阅.大家看完所有的,如果整理出文章的,麻烦知会一下,方便学习. 1.Redis是什么? 十五分钟介绍 Redis数据结构 Redi ...

  10. 老是出现dispolse 找不到合适的方法来重写

    复制到输出目录:不复制 生成操作:编译