转载整理:显式游标和隐式游标和动态游标

隐式游标 sql


如前所述,DML操做和单行SELECT语句会使用隐式游标,它们是: 
* 插入操做:INSERT。 
* 更新操做:UPDATE。 
* 删除操做:DELETE。 
* 单行查询操做:SELECT ... INTO ...。 
当系统使用一个隐式游标时,能够经过隐式游标的属性来了解操做的状态和结果,进而控制程序的流程。隐式游标可使用名字SQL来访问,但要注意,经过SQL游标名老是只能访问前一个DML操做或单行SELECT操做的游标属性。因此一般在刚刚执行完操做以后,当即使用SQL游标名来访问属性。游标的属性有四种,以下所示:数据库

sql%found (布尔类型,默认值为null)函数

sql%notfound(布尔类型,默认值为null)工具

sql%rowcount(数值类型默认值为0)oop

sql%isopen(布尔类型)学习

当执行一条DML语句后,DML语句的结果保存在四个游标属性中,这些属性用于控制程序流程或者了解程序的状态。当运行DML语句时,PL/SQL打开一个内建游标并处理结果,游标是维护查询结果的内存中的一个区域,游标在运行DML语句时打开,完成后关闭。隐式游标只使用SQL%FOUND,SQL%NOTFOUND,SQL%ROWCOUNT三个属性.SQL%FOUND,SQL%NOTFOUND是布尔值,SQL%ROWCOUNT是整数值。fetch

SQL%FOUND和SQL%NOTFOUNDspa

在执行任何DML语句前SQL%FOUND和SQL%NOTFOUND的值都是NULL,在执行DML语句后,SQL%FOUND的属性值将是:指针

     . TRUE :INSERTcode

  . TRUE :DELETE和UPDATE,至少有一行被DELETE或UPDATE.

  . TRUE :SELECT INTO至少返回一行

 当SQL%FOUND为TRUE时,SQL%NOTFOUND为FALSE。

 SQL%ROWCOUNT

  在执行任何DML语句以前,SQL%ROWCOUNT的值都是NULL,对于SELECT INTO语句,若是执行成功,SQL%ROWCOUNT的值为1,若是没有成功或者没有操做(如update、insert、delete为0条),SQL%ROWCOUNT的值为0,而对于update和delete来讲表示游标所检索数据库行的个数即更新或者删除的行数。

SQL%ISOPEN

  SQL%ISOPEN是一个布尔值,若是游标打开,则为TRUE, 若是游标关闭,则为FALSE.对于隐式游标而言SQL%ISOPEN老是FALSE,这是由于隐式游标在DML语句执行时打开,结束时就当即关闭。

 最后咱们来讲一下隐式游标中SELECT..INTO 语句,当执行的时候会有三种可能:

(1).结果集只含有一行,且select是成功的

 (2).没有查询到任何结果集,引起NO_DATA_FOUND异常

 (3).结果集中含有两行或者更多行,引起TOO_MANY_ROWS异常。

例子:

 
BEGIN
     UPDATE exchangerate SET rate=7 where quarter='2011Q1';
     DBMS_output.put_line('游标所影响的行数:'||SQL%rowcount);
      if SQL%NotFound then

         DBMS_output.put_line('NotFound为真');

         DBMS_output.put_line('NofFound为假');   
      end if;
      if SQL%Found then
           DBMS_output.put_line('Found为真');
      else
           DBMS_output.put_line('Found为假');
      end if;
      if SQL%isopen then
           DBMS_output.put_line('isOpen为真');
      else
           DBMS_output.put_line('isOpen为假');
      end if;
END;

 

动态游标

与游标同样,动态游标(游标变量)也是一个指向多行查询结果集合中当前数据行的指针。但与游标不一样的是,游标变量是动态的,而游标是静态的。

游标只能与指定的查询相连,即固定指向一个查询的内存处理区域,而游标变量则可与不一样的查询语句相连,它能够指向不一样查询语句的内存处理区域(但不能同时指向多个内存处理区域,在某一时刻只能与一个查询语句相连),只要这些查询语句的返回类型兼容便可。

 

 

  1. DECLARE  
  2.    --定义一个游标数据类型  
  3.    TYPE emp_cursor_type IS REF CURSOR;  
  4.    --声明一个游标变量  
  5.    c1 EMP_CURSOR_TYPE;  
  6.    --声明两个记录变量  
  7.    v_emp_record employees%ROWTYPE;  
  8.    v_reg_record regions%ROWTYPE;  
  9.   
  10. BEGIN  
  11.    OPEN c1 FOR SELECT * FROM employees WHERE department_id = 20;  
  12.    LOOP  
  13.       FETCH c1 INTO v_emp_record;  
  14.       EXIT WHEN c1%NOTFOUND;  
  15.       DBMS_OUTPUT.PUT_LINE(v_emp_record.first_name||'的雇佣日期是'  
  16.                             ||v_emp_record.hire_date);  
  17.    END LOOP;  
  18.    --将同一个游标变量对应到另外一个SELECT语句  
  19.    OPEN c1 FOR SELECT * FROM regions WHERE region_id IN(1,2);  
  20.    LOOP  
  21.       FETCH c1 INTO v_reg_record;  
  22.       EXIT WHEN c1%NOTFOUND;  
  23.       DBMS_OUTPUT.PUT_LINE(v_reg_record.region_id||'表示'  
  24.                             ||v_reg_record.region_name);  
  25.    END LOOP;  
  26.    CLOSE c1;  
  27. END;  

显式游标:

游标的定义和操做 
游标的使用分红如下4个步骤。 

1.声明游标 


在DECLEAR部分按如下格式声明游标: 
CURSOR 游标名[(参数1 数据类型[,参数2 数据类型...])] 
IS SELECT语句; 
参数是可选部分,所定义的参数能够出如今SELECT语句的WHERE子句中。若是定义了参数,则必须在打开游标时传递相应的实际参数。 
SELECT语句是对表或视图的查询语句,甚至也能够是联合查询。能够带WHERE条件、ORDER BY或GROUP BY等子句,但不能使用INTO子句。在SELECT语句中可使用在定义游标以前定义的变量。

 
2.打开游标 


在可执行部分,按如下格式打开游标: 
OPEN 游标名[(实际参数1[,实际参数2...])]; 
打开游标时,SELECT语句的查询结果就被传送到了游标工做区。 


3.提取数据 


在可执行部分,按如下格式将游标工做区中的数据取到变量中。提取操做必须在打开游标以后进行。 
FETCH 游标名 INTO 变量名1[,变量名2...]; 
或 
FETCH 游标名 INTO 记录变量; 
游标打开后有一个指针指向数据区,FETCH语句一次返回指针所指的一行数据,要返回多行需重复执行,可使用循环语句来实现。控制循环能够经过判断游标的属性来进行。 
下面对这两种格式进行说明: 
第一种格式中的变量名是用来从游标中接收数据的变量,须要事先定义。变量的个数和类型应与SELECT语句中的字段变量的个数和类型一致。 
第二种格式一次将一行数据取到记录变量中,须要使用%ROWTYPE事先定义记录变量,这种形式使用起来比较方便,没必要分别定义和使用多个变量。 
定义记录变量的方法以下: 
变量名 表名|游标名%ROWTYPE; 
其中的表必须存在,游标名也必须先定义。 


4.关闭游标 


CLOSE 游标名; 
显式游标打开后,必须显式地关闭。游标一旦关闭,游标占用的资源就被释放,游标变成无效,必须从新打开才能使用。 

如今经过一个例子来学习一下显示游标的使用方法:

有一个表原来结构是以下的

create table EXCHANGERATE
(
  QUARTER      VARCHAR2(20),
  RATE         NUMBER(10,4),
  DESCRIPTION  VARCHAR2(900),
  ID           VARCHAR2(10) not null,
  CURRENCY     VARCHAR2(100)
)

 

这是一个汇率表里面维护着的是季度 币种和汇率的关系,如今有一个新的需求是在原来表的基础上增长一列名字为currentmonth,变为季度、季度中月份、 币种和汇率的关系,

而且使原来每一个季度对应的币种和汇率变成每一个季度 对应该季度月份 币种和汇率,每月的默认值为原来季度对应的值。

例如 原来 2013Q2 CNY 6.2

如今咱们要变为2013Q2 2013-04 CNY 6.2  2013Q2 2013-05 CNY 6.2

2013Q2 2013-06 CNY 6.2  三条记录。

经过分析以上需求,咱们首先要增长一列:

alter table exchangerate add currentmonth varchar2(20);

而后咱们经过在匿名块中经过显示游标来实现以上需求:

 1 declare
 2 
 3  v_year varchar2(20);
 4  v_month number;
 5  p_rate exchangerate%rowtype;
 6  cursor c_rate is  select * from exchangerate t where t.currentmonth is null;
 7  begin
 8    open c_rate;
 9     loop
10     fetch c_rate into p_rate;
11     v_year:=substr(p_rate.quarter, 0, 4);
12     v_month:=(to_number(substr(p_rate.quarter,6,1)) - 1) * 3;
13     for i in 1 .. 3 loop
14 
15     insert into exchangerate(id,quarter,currentmonth,rate,currency,Description)
16     values(SEQUENCE_EXCHANGERATE.nextval,p_rate.quarter,
17            to_char(to_date(v_year||(v_month+i),'yyyyMM'),'yyyy-MM'),p_rate.rate,p_rate.currency,p_rate.description);
18      
19     end loop; 
20     exit when c_rate%notfound;
21    end loop;
22    close c_rate;
23  end;
24  /
25 复制代码
26  咱们把上面的例子有游标的for循环来改写一下。
27 
28  
29 
30 显式游标的for循环
31 
32 复制代码
33 declare
34 
35  v_year varchar2(20);
36  v_month number;
37 
38  cursor c_rate is  select * from exchangerate t where t.currentmonth is null;
39 
40  begin
41 
42    for p_rate in c_rate loop
43 
44    v_year:=substr(p_rate.quarter, 0, 4);
45 
46    v_month:=(to_number(substr(p_rate.quarter,6,1)) - 1) * 3;
47 
48     for i in 1 .. 3 loop
49 
50     insert into exchangerate(id,quarter,currentmonth,rate,currency,Description)
51     values(SEQUENCE_EXCHANGERATE.nextval,p_rate.quarter,
52            to_char(to_date(v_year||(v_month+i),'yyyyMM'),'yyyy-MM'),p_rate.rate,p_rate.currency,p_rate.description);
53 
54     end loop; 
55 
56    end loop;
57 
58  end;
59 
60   /

 

 

咱们能够看到游标FOR循环确实很好的简化了游标的开发,咱们不在须要open、fetch和close语句,不在须要用%FOUND属性检测是否到最后一条记录,这一切Oracle隐式的帮咱们完成了。

 

隐式游标的for循环

 

 1 declare
 2 
 3  v_year varchar2(20);
 4  v_month number;
 5 
 6  begin
 7 
 8    for p_rate in (select * from exchangerate t where t.currentmonth is null) loop
 9 
10    v_year:=substr(p_rate.quarter, 0, 4);
11    v_month:=(to_number(substr(p_rate.quarter,6,1)) - 1) * 3;
12     for i in 1 .. 3 loop
13 
14       insert into exchangerate(id,quarter,currentmonth,rate,currency,Description) 
15         values(SEQUENCE_EXCHANGERATE.nextval,p_rate.quarter,
16                to_char(to_date(v_year||(v_month+i),'yyyyMM'),'yyyy-MM'),p_rate.rate,p_rate.currency,p_rate.description);
17 
18     end loop; 
19 
20    end loop;
21 
22  end;
23 /

 

 

显示游标中游标参数的传递


例子:就以上面的表来讲 加入咱们在定义游标时不肯定查询条件中的值,这时咱们能够经过游标参数来解决

 1 declare
 2 
 3  v_year varchar2(20);
 4  v_month number;
 5  p_rate exchangerate%rowtype;
 6 
 7  cursor c_rate(p_quarter varchar2) --声明游标带参数
 8  is  
 9     select * from exchangerate t where t.quarter<=p_quarter;
10 
11  begin
12    open c_rate(p_quarter=>'2011Q3');--打开游标,传递参数值
13     loop
14 
15     fetch c_rate into p_rate;
16     
17     update exchangerate set rate=p_rate.rate+1 where id=p_rate.id;
18 
19 
20     exit when c_rate%notfound;
21 
22    end loop;
23 
24    close c_rate;
25 
26  end;

 

游标变量

 游标是数据库中一个命名的工做区,当游标被声明后,他就与一个固定的SQL想关联,在编译时刻是已知的,是静态的.它永远指向一个相同的查询工做区.
 游标变量是动态的能够在运行时刻与不一样的SQL语句关联,在运行时能够取不一样的SQL语句.它能够引用不一样的工做区.

如何定义游标类型

 

TYPE ref_type_name IS REF CURSOR

[RETURN return_type];

声明游标变量

cursor_name ref_type_name;

 

ref_type_name 是后面声明游标变量时要用到的咱们的游标类型(自定义游标类型,即CURSOR是系统默认的,ref_type_name是咱们定义的 );

return_type表明数据库表中的一行,或一个记录类型

TYPE ref_type_name IS REF CURSOR RETURN EMP%TYPE

RETURN 是可选的,若是有是强类型,能够减小错误,若是没有return是弱引用,有较好的灵活性.

游标变量的操做

 例子:

 1 declare
 2 
 3  v_year varchar2(20);
 4  v_month number;
 5  p_rate exchangerate%rowtype;
 6 
 7  type  rate is ref cursor;--定义游标变量
 8   c_rate rate; --声明游标变量
 9   
10  begin
11    
12    
13    open c_rate for select * from exchangerate t where t.quarter='2011Q3';--打开游标变量    loop
14 
15     fetch c_rate into p_rate;--提取游标变量
16     
17     update exchangerate set rate=p_rate.rate+1 where id=p_rate.id;
18 
19     exit when c_rate%notfound;
20 
21    end loop;
22   
23   --将同一个游标变量对应到另外一个SELECT语句
24    
25    open c_rate for select * from exchangerate t where t.quarter='2011Q2';--打开游标变量    loop
26 
27     fetch c_rate into p_rate;--提取游标变量
28     
29     update exchangerate set rate=p_rate.rate-1 where id=p_rate.id;
30 
31     exit when c_rate%notfound;
32 
33    end loop;
34    close c_rate;--关闭游标变量
35 
36  end;

 

 

游标表达式

Oracle在SQL语言中提供了一个强有力的工具:游标表达式。一个游标表达式从一个查询中返回一个内嵌的游标。在这个内嵌游标的结果集中,每一行数据包含了在SQL查询中的可容许的数值范围;它也能包含被其余子查询所产生的游标。

所以,你可以使用游标表达式来返回一个大的和复杂的,从一张或多张表获取的数据集合。游标表达式的复杂程度,取决于查询和结果集。然而,了解全部从Oracle RDBMS提取数据的可能途径,还有大有好处的。

你可以在如下任何一种状况使用游标表达式:

   (1)、 显式游标声明

   (2)、动态SQL查询。

   (3)、REF CURSOR 声明和变量。

你不能在一个隐式查询中使用游标表达式。

游标表达式的语法是至关简单的:

    CURSOR (查询语句)

Oracle从父游标或外围游标那里检取包含游标表达式的数据行时,Oracle就会隐式地打开一个内嵌的游标,这个游标就是被上述的游标表达式所定义。在如下状况发生时,这个内迁游标将会被关闭:

    (1)、你显式地关闭这个游标。

    (2)、外围或父游标被从新执行,关闭或撤销。

    (3)、当从父游标检取数据时,发生异常。内嵌游标就会与父游标一块儿被关闭。

 使用游标表达式

你能够经过两种不一样的,可是很是有用的方法来使用游标表达式:

   1.  在一个外围查询中把字查询做为一列来检取数据。

   2.  把一个查询转换成一个结果集,而这个结果集就能够被当成一个参数传递给一个流型或变换函数。

例子:

 1 CREATE OR REPLACE PROCEDURE emp_report(p_locid NUMBER)
 2 IS
 3     TYPE refcursor IS REF CURSOR;
 4     CURSOR all_in_one IS
 5         SELECT l.city, CURSOR(
 6             SELECT  d.department_name, CURSOR (
 7                 SELECT e.last_name
 8                 FROM employees e
 9                 WHERE e.DEPARTMENT_ID = d.DEPARTMENT_ID
10             ) as ename
11             FROM departments d
12             WHERE d.LOCATION_ID = l.LOCATION_ID
13         ) as dname
14         FROM locations l
15         WHERE l.location_id = p_locid;
16 departments_cur refcursor;
17 employees_cur refcursor;
18 v_city locations.city%type;
19 v_dname departments.department_name%type;
20 v_ename employees.last_name%type;
21 i integer :=1;
22 j integer :=1;
23 k integer :=1;
24 BEGIN
25     OPEN all_in_one;
26     LOOP    
27     FETCH all_in_one INTO v_city, departments_cur;
28     EXIT WHEN all_in_one%NOTFOUND;
29                                                   LOOP
30         FETCH departments_cur INTO  v_dname, employees_cur;
31         EXIT WHEN departments_cur%NOTFOUND;
32         LOOP
33             FETCH employees_cur INTO v_ename;
34             EXIT WHEN employees_cur%NOTFOUND;
35             dbms_output.put_line(i || ' , ' || j || ' , ' || k || '----' || v_city || ' ,' || v_dname || ' ,' || v_ename );
36             k := k + 1;
37         END LOOP;
38         j := j + 1;
39     END LOOP;
40     i := i + 1;
41     END LOOP;
42 END;
43 /