数据提取 -- 游标

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

声明游标: 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. 研究Dropbox Server端文件系统

    一.传统文件系统 可以理解成两部分:1.真正的storage区,被分割成n个扇区:2.文件系统,其实就是一个FAT表. 二.Dropbox的文件系统 例如,一个modeo.mov的文件,大小为15M. ...

  2. JavaScript之With语句讲解

    有了With 语句,在存取对象属性和方法时就不用重复指定参考对象,在 With 语句块中,凡是 JavaScript 不识别的属性和方法都和该语句块指定的对象有关.With 语句的语法格式如下所示:W ...

  3. js获取,设置FCKeditor内容

    // 获取编辑器中HTML内容 function getEditorHTMLContents(EditorName) {     var oEditor = FCKeditorAPI.GetInsta ...

  4. Base64编解码Android和ios的例子,补充JNI中的例子

    1.在Android中java层提供了工具类:android.util.Base64; 里面都是静态方法,方便直接使用: 使用方法如下: // Base64 编码: byte [] encode =  ...

  5. asp.net word ecxel类型文件在线预览

    asp.net word ecxel类型文件在线预览 首先得引用COM: Microsoft Excel 10 Object Library Microsoft Word 10 Object Libr ...

  6. ibatis中iterate的用法(conjunction="or" ",")

    例子一 查询条件dto public class queryCondition{ private String[] stuIds; private String name;} 查询sqlMap < ...

  7. MySQL中文全文索引插件 mysqlcft 1.0.0 安装使用文档[原创]

    [文章+程序 作者:张宴 本文版本:v1.0 最后修改:2008.07.01 转载请注明原文链接:http://blog.zyan.cc/post/356/] MySQL在高并发连接.数据库记录数较多 ...

  8. Tech Stuff - Mobile Browser ID (User-Agent) Strings

    Tech Stuff - Mobile Browser ID (User-Agent) Strings The non-mobile stuff is here (hint: you get jerk ...

  9. 支持DISTINCT的通用分页存储过程(SQL2005)

    /****** 对象: StoredProcedure [dbo].[P_CommonPagination] 脚本日期: 07/22/2009 10:22:01 ******/ SET ANSI_NU ...

  10. 服务接口API限流 Rate Limit

    一.场景描述 很多做服务接口的人或多或少的遇到这样的场景,由于业务应用系统的负载能力有限,为了防止非预期的请求对系统压力过大而拖垮业务应用系统. 也就是面对大流量时,如何进行流量控制? 服务接口的流量 ...