数据提取 -- 游标

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

声明游标: 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. Centos编译安装PHP 5.5笔记

    本篇是在 Centos 6.4 32bit 下编译安装 php 5.5.5 的笔记,接上篇 Centos编译安装Apache 2.4.6笔记.php 5.5.x 和 centos 源里面的 php 5 ...

  2. 引擎设计跟踪(九.8) Gizmo helper实现与多国语言

    最近把gizmo helper的绘制做好了. 1.为了复用代码,写了utility来创建sphere, cube, cylinder, plane, ring(line), circle(solid) ...

  3. 常量折叠 const folding

    http://bbs.byr.cn/#!article/CPP/86336?p=1 下列代码给出输出结果: #include"stdafx.h" #include <iost ...

  4. hive的学习入门(飞进数据仓库的小蜜蜂)

    前言 hive是构建在Hadoop上的数据仓库平台,其设计目标是:使Hadoop上的数据操作与传统的SQL结合,让熟悉sql的开发人员能够轻松的像Hadoop平台迁移. Hive是Facebook的信 ...

  5. VMware workstation 的虚拟机中再安装workstation

    在VMware workstation 10中运行的虚拟机中再安装workstation软件时,启动时会出现不断重启的故障, 解决办法: 在宿主虚拟机的.vmx文件中添加一行 monitor_cont ...

  6. cf div2 238 c

    C. Unusual Product time limit per test 1 second memory limit per test 256 megabytes input standard i ...

  7. POJ 2010

    Moo University - Financial Aid Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 4235   A ...

  8. laravel where中多条件查询

    1. http://www.mobanstore.com/doc/bianchengkaifa/119.html //初学laravel 发现他的查询构造器很好用 //如下 $user = DB::t ...

  9. php string转换为int

    本身 var_dump : string(3) "002" 本身 is_numeric : bool(true) 本身 转换为数字 : int(2) 本身 转换为数字变量 : in ...

  10. hdu 4726 Kia's Calculation

    思路:刚开始想复杂了. 看解题报告后才知道这题挺简单的,看来还是要多训练啊!!! 单独处理首位的数字,不能为0.其他的就好处理了,从大到小依次找下去就可以了…… 代码如下: #include<i ...