-
 -异常的作用域
 
-
 -结论:如果在语句块中定义一个异常,该异常是本语句
 
-
 -块专用的。但是,在语句块嵌套的情况下,外部语句块中
 
-
 -所定义的任何异常都适用于内部语句块
 
-
 -用户定义异常
 
-
 -作用:用来表示违反业务规则的情况。
 
-
 -
 -要使用用户定义异常,必须首先进行声明。
 
-
 -用户定义异常在PL
 /
 SQL语句块的声明部分进行声明: 
 
DECLARE 
 
    exception_name  
 EXCEPTION;
 
-
 -而用户定义异常必须显式抛出。也就是说,
 
-
 -需要自己在程序中抛出异常
 
DECLARE
 
       exception_name 
 EXCEPTION;
 
BEGIN
 
       ...
 
      IF  condition  
 THEN
 
         RAISE   exception_name;
 
      
 ELSE
 
       ...
 
      
 END IF;
 
EXCEPTION
 
       
 WHEN  exception_name  
 THEN
 
           error
 -processing statements;
 
END;
 
-
 -总结:
 3步:声明
 -
 -
 -抛出
 -
 -
 -捕获
 
-
 -示例
 3:
 
-
 -使用用户定义异常处理输入的学生编号为负的错误情况
 
DECLARE
 
    v_student_id student.student_id%
 type :
 = &sv_student_id;
 
    v_total_courses 
 NUMBER;
 
    
 -
 -
 1、声明异常对象。代表生编号为负的错误情况
 
    e_invalid_id  
 EXCEPTION;
 
BEGIN
 
    
 -
 -检查学生编号是否违反业务规则
 
    if v_student_id 
 < 
  0 
 then
 
      
 -
 -
 2、抛出异常,表示违法了业务规则
 
      raise e_invalid_id;
 
    
 else  
 
      
 -
 -正常的业务处理
 
      
 select 
  count(
 *)
 
        
 into v_total_courses
 
        
 from enrollment
 
        
 where student_id
 =v_student_id;
 
     dbms_output.put_line(v_total_courses);   
 
    
 end if;
 
exception 
  -
 -
 3、捕获异常
 
   
 when e_invalid_id 
  then
 
     dbms_output.put_line(
 '学生编号不能为负的');   
 
end;
 
-
 -异常传播
 
-
 -研究在plsql块的
 3个部分产生异常对象时,该对象
 
-
 -如何向外传播并得到处理。
 
-
 -第一种情况:在
 begin...
 end部分抛出异常
 
-
 -该异常首先由本块的
 exception部分来处理。如果本块
 
-
 -的
 exception部分不处理,那么由外部块的
 exception
 
-
 -部分来处理;如果外部块也不处理,那么最终由系统
 
-
 -来处理:将程序终止,然后打印系统错误消息。
 
-
 -第二种情况:在
 declare部分抛出异常
 
-
 -本块的
 exception部分不能处理该异常。只能由外部块
 
-
 -的
 exception部分来处理;如果外部块也不处理,
 
-
 -那么最终由系统来处理
 
-
 -示例
 6:
 
DECLARE
 
    v_test_var 
 CHAR(
 3):
 = 
 'ABCDE';
 
BEGIN
 
    DBMS_OUTPUT.PUT_LINE (
 'This is a test');
 
EXCEPTION
 
    
 WHEN INVALID_NUMBER 
  OR VALUE_ERROR 
 THEN
 
       DBMS_OUTPUT.PUT_LINE (
 'An error has occurred');
 
END;
 
-
 -改进上例,抓住异常
 
begin
 
   
 DECLARE
 
     v_test_var 
 CHAR(
 3) :
 = 
 'ABCDE';
 
   
 BEGIN
 
     DBMS_OUTPUT.PUT_LINE(
 'This is a test');
 
   
 EXCEPTION
 
     
 WHEN INVALID_NUMBER 
  OR VALUE_ERROR 
 THEN
 
       DBMS_OUTPUT.PUT_LINE(
 'An error has occurred');
 
   
 END;
 
EXCEPTION
 
   
 WHEN INVALID_NUMBER 
  OR VALUE_ERROR 
 THEN
 
     DBMS_OUTPUT.PUT_LINE(
 '外部块抓住了');
 
end;
 
-
 -第三种情况:在
 exception部分抛出异常
 
-
 -本块的
 exception部分不能处理该异常。只能由外部块
 
-
 -的
 exception部分来处理;如果外部块也不处理,
 
-
 -那么最终由系统来处理
 
-
 -示例
 8:
 
DECLARE
 
    v_test_var 
 CHAR(
 3) :
 = 
 'ABC';
 
BEGIN
 
    v_test_var :
 = 
 '1234';
 
    DBMS_OUTPUT.PUT_LINE (
 'v_test_var: '||v_test_var);
 
EXCEPTION
 
    
 WHEN INVALID_NUMBER 
  OR VALUE_ERROR 
 THEN
 
      
 -
 -如何处理该异常?
 
       v_test_var :
 = 
  'ABCD'; 
 
       DBMS_OUTPUT.PUT_LINE (
 'An error has occurred');
 
END;
 
-
 -改进上例,抓住异常
 
begin
 
   
 DECLARE
 
     v_test_var 
 CHAR(
 3) :
 = 
 'ABC';
 
   
 BEGIN
 
     v_test_var :
 = 
  '1234';
 
     DBMS_OUTPUT.PUT_LINE(
 'v_test_var: ' || v_test_var);
 
   
 EXCEPTION
 
     
 WHEN INVALID_NUMBER 
  OR VALUE_ERROR 
 THEN
 
       
 -
 -如何处理该异常?
 
       v_test_var :
 = 
  'ABCD';
 
       DBMS_OUTPUT.PUT_LINE(
 'An error has occurred');
 
   
 END;
 
exception
 
   
 WHEN INVALID_NUMBER 
  OR VALUE_ERROR 
 THEN
 
     DBMS_OUTPUT.PUT_LINE(
 '外部块抓住了');
 
end;
 
-
 -异常高级概念
 
-
 -
 1、使用raise_application_error过程处理用户定义异常
 
-
 -RAISE_APPLICATION_ERROR(error_number, error_message);
 
-
 -参数:error_number:错误编号,取值范围:
 -
 20999到
 -
 20000
 
-
 -error_message:错误消息,最长
 2048字节。
 
-
 -该过程直接在控制台打印错误编号和错误消息
 
-
 -使用该过程处理学生编号为负的异常情况
 
DECLARE
 
    v_student_id student.student_id%
 type :
 = &sv_student_id;
 
    v_total_courses 
 NUMBER;
 
    
 -
 -
 1、声明异常对象。代表生编号为负的错误情况
 
    
 -
 -e_invalid_id  
 EXCEPTION;
 
BEGIN
 
    
 -
 -检查学生编号是否违反业务规则
 
    if v_student_id 
 < 
  0 
 then
 
      
 -
 -
 2、抛出异常,表示违法了业务规则
 
     
 -
 - raise e_invalid_id
 
     raise_application_error(
 -
 20000,
 '学生编号不能为负的');
 
    
 else  
 
      
 -
 -正常的业务处理
 
      
 select 
  count(
 *)
 
        
 into v_total_courses
 
        
 from enrollment
 
        
 where student_id
 =v_student_id;
 
     dbms_output.put_line(v_total_courses);   
 
    
 end if;
 
-
 -
 exception 
 -
 -
 3、捕获异常
 
-
 - 
  when e_invalid_id 
 then
 
   
 -
 -  dbms_output.put_line(
 '学生编号不能为负的');   
 
end;
 
-
 -本例中不包含异常的名称、RAISE语句和
 
-
 -错误处理部分
 
-
 -raise_application_error过程的问题:
 
-
 -
 1、错误消息的显示格式和系统错误消息的显示格式一样,
 
-
 -可能用户不能接受
 
-
 -
 2、需要专人维护错误编号和错误消息之间的对应关系
 
-
 -一个编译指令是针对PL
 /
 SQL编译器的特殊指令。
 
-
 -当程序编译时处理编译指令
 
-
 -
 2、exception_init编译指令
 
-
 -通过使用EXCEPTION_INIT编译指令,你可以把某个
 
-
 -Oracle错误编号和某个用户定义异常的名称建立关联。
 
-
 -在建立关联之后,就可以引用这个错误,
 
-
 -并为它编写处理程序
 
-
 -EXCEPTION_INIT编译指令出现在语句块的声明部分,
 
-
 -如下所示: 
 
DECLARE 
 
   exception_name  
 EXCEPTION; 
 
   PRAGMA  EXCEPTION_INIT(exception_name,error_code);
 
-
 -示例
 5:根据用户提供的邮编从ZIPCODE表中删除相应的
 
-
 -记录,并在屏幕上显示邮编已被删除的消息
 
-
 -使用
 06870作为v_zip的值运行这个范例
 
DECLARE
 
    v_zip ZIPCODE.ZIP%
 TYPE :
 = 
 '&sv_zip';
 
BEGIN
 
    
 DELETE 
  FROM zipcode
 
     
 WHERE zip 
  = v_zip;
 
     DBMS_OUTPUT.PUT_LINE (
 'Zip '||v_zip||
 ' has been deleted');
 
    
 COMMIT;
 
END;
 
-
 -运行结果:
 
-
 -ORA
 -
 02292: integrity 
 constraint (STUDENT.STU_ZIP_FK) violated 
 - child record 
 found
 
-
 -ORA
 -
 06512: 
 at line 
  5
 
-
 -因为外键错误没有定义成内置异常,因此是不能使用
 exception
 
-
 -来捕获的。
 
-
 -要处理外键错误,必须使用编译指令
 
DECLARE
 
    v_zip ZIPCODE.ZIP%
 TYPE :
 = 
 '&sv_zip';
 
    e_child_exists 
 exception;
 
    pragma exception_init(e_child_exists,
 -
 2292);
 
BEGIN
 
    
 DELETE 
  FROM zipcode
 
     
 WHERE zip 
  = v_zip;
 
     DBMS_OUTPUT.PUT_LINE (
 'Zip '||v_zip||
 ' has been deleted');
 
    
 COMMIT;
 
exception
 
   
 when e_child_exists 
  then
 
     dbms_output.put_line(
 '先删除一些学生');   
 
END;
 
-
 -
 3、两个函数
 
-
 -为了改进OTHERS异常处理程序,Oracle提供了两个内置函数:
 SQLCODE和SQLERRM
 
-
 -
 SQLCODE函数:返回Oracle错误编号
 
-
 -SQLERRM函数:返回错误消息。返回消息的最大长度是
 512字节
 
-
 -
 07458作为邮政编码的值
 
DECLARE
 
    v_zip      VARCHAR2(
 5) :
 = 
 '&sv_zip';
 
    v_city     VARCHAR2(
 15);
 
    v_state    
 CHAR(
 2);
 
    v_err_code 
 NUMBER;
 
    v_err_msg  VARCHAR2(
 200);
 
BEGIN
 
    
 SELECT city, 
  state
 
      
 INTO v_city, v_state
 
      
 FROM zipcode
 
     
 WHERE zip 
  = v_zip;
 
     DBMS_OUTPUT.PUT_LINE (v_city||
 ',  '||v_state);
 
EXCEPTION
 
    
 WHEN  OTHERS 
  THEN
 
      v_err_code :
 = 
  SQLCODE;
 
      v_err_msg  :
 = SUBSTR(SQLERRM, 
  1, 
 200);
 
      DBMS_OUTPUT.PUT_LINE (
 'Error code: '||v_err_code);
 
      DBMS_OUTPUT.PUT_LINE (
 'Error message: '||v_err_msg);
 
END;
 
-
 -游标基础
 
-
 -记录类型
 
-
 -它是一个复合数据类型,其内部由多个字段(元素)构成。
 
-
 -复合数据类型的特点是:该类型的一个变量就可以保存
 
-
 -多个值。
 
-
 -记录类型专门设计用来保存表或者游标中的一行。
 
-
 -PL
 /
 SQL支持三种类型的记录:
 
-
 -基于表的(
 table
 -based)、基于游标的(
 cursor
 -based)
 
-
 -以及程序员定义的(programmer
 -
 defined)
 
-
 -基于表的记录就是该记录的结构来自于数据库表中所有的列
 
-
 -基于游标的记录就是记录的结构来自于定义游标的
 select列表。
 
-
 -为创建一个基于表或者基于游标的记录,
 
-
 -可以使用%ROWTYPE属性。语法: 
 
-
 - record_name 
 table_name 
  or 
 cursor_name%ROWTYPE
 
-
 -示例
 1:使用记录保存student表中的一行
 
declare
 
   
 -
 -声明基于student表的记录类型以及记录变量
 
   r_student student%rowtype;
 
begin
 
   
 -
 -将学生
 156保存到记录中
 
   
 select 
  *
 
     
 into r_student
 
     
 from student
 
     
 where student_id
 =
 156;
 
   
 -
 -取出记录中各个字段的值。语法:记录变量名.字段名    
 
   dbms_output.put_line(r_student.first_name);
 
   dbms_output.put_line(r_student.last_name);
 
   dbms_output.put_line(r_student.phone);
 
end;
 
-
 -处理显式游标
 
-
 -
 4个步骤:声明、打开、检索和关闭
 
-
 -示例
 4:声明一个游标,保存
 5个学生的姓名。检索游标,
 
-
 -将
 5个学生的姓名打印到屏幕上
 
declare
 
   
 -
 -
 1、声明
 
   
 cursor c_student_name 
  is
 
     
 select first_name,last_name
 
       
 from student
 
       
 where rownum 
  <
 = 
 5;
 
   
 -
 -声明基于游标的记录,以用于游标检索
 
   r_student c_student_name%rowtype;
 
begin
 
   
 -
 -
 2、打开
 
   
 open c_student_name;  
 
   loop
 
     
 -
 -
 3、检索。将拿到的行存到记录中
 
     
 fetch c_student_name 
  into r_student; 
 
     
 -
 -循环退出条件
 
     exit 
 when c_student_name%notfound;
 
     
 -
 -对拿到的行进行处理
 
     dbms_output.put_line(r_student.first_name||
 
       
 ' '||r_student.last_name);
 
   
 end loop; 
 
   
 -
 -
 4、关闭
 
   
 close c_student_name; 
 
end;
 
-
 -用户定义记录
 
-
 -用户定义记录是基于程序员定义的记录类型的。
 
-
 -首先,声明一种记录类型,然后基于所定义的记录类型
 
-
 -来声明记录变量。语法如下: 
 
-
 -
 type  type_name  
 is record 
 
 ( field_name1  datatype1 ,
 
   field_name2  datatype2, 
 
   ……
 
   field_namen  datatypen) ;
 
  record_name  type_name;
 
-
 -示例
 6:使用用户定义记录保存
 102号老师的姓名和
 
-
 -所教班级的数量,然后打印记录的内容
 
declare
 
   
 -
 -声明记录类型
 
   
 type instructor_info_type 
  is record(
 
     fname instructor.first_name%
 type,
 
     lname instructor.last_name%
 type,
 
     sections 
 number
 
   );
 
-
 -声明记录变量
 
 r_instructor instructor_info_type;
 
begin
 
   
 -
 -将
 102号老师的姓名和所教班级的数量保存到记录中
 
   
 select first_name,last_name,
 count(
 *)
 
     
 into r_instructor
 
     
 from instructor i,
 section s
 
     
 where i.instructor_id 
  = s.instructor_id
 
       
 and i.instructor_id
 =
 102
 
       
 group 
  by first_name,last_name;
 
   dbms_output.put_line(r_instructor.fname||
 ' '||
 
      r_instructor.lname||
 '教了'||r_instructor.sections||
 '班');
 
end; 
 
-
 -使用游标属性:
 4个
 
-
 -
 1)%notfound: 如果前面的
 fetch操作没有拿到行,则为
 true,否则为
 false
 
-
 -
 2)%
 found: 如果前面的
 fetch操作拿到了行,则为
 true,否则为
 false
 
-
 -
 3)%rowcount: 取值
 >
 =
 0。累计
 fetch操作一共拿到的行的数量
 
-
 -
 4)%isopen: 如果游标是打开的,则为
 true,否则为
 false
 
-
 -常见使用方法:
 
-
 -
 1)%notfound主要用来退出游标检索循环
 
  
 -
 -   exit 
  when c_student%notfound; 
 
   
 -
 - 
  end  loop; 
 
-
 -
 2)%isopen主要用在
 exception中,判断游标是否关闭,
 
-
 -如果没有则关掉它
 
- 
 exception 
 
        
 when others  
 then 
 
      if c_student%isopen   
 then 
 
            
 close c_student; 
 
      
 end if; 
 
end;
 
-
 -
 3)%rowcount主要用来判断是否从游标中拿到了行或者
 
-
 -统计拿到的行的数量
 
-
 -注意:
 
-
 -通过
 SQL前缀,就可以操作隐式游标的游标属性,
 
-
 -如
 SQL%ROWCOUNT
 
-
 -对于隐式游标,其%isopen属性永远为
 false
 
-
 -示例
 9:使用%
 found属性退出游标检索循环。
 
-
 -使用%rowcount属性统计
 fetch操作拿到的行的数量
 
declare
 
   v_sid student.student_id%
 type;
 
   
 cursor c_student 
  is
 
     
 select student_id
 
       
 from student
 
       
 where student_id
 <
 110;
 
begin
 
   
 open c_student;
 
   loop
 
     
 fetch c_student 
  into v_sid;
 
     
 -
 -判断是否从游标中拿到行
 
     if c_student%
 found 
  then
 
       dbms_output.put_line(v_sid);
 
       dbms_output.put_line(
 'fetch拿到了'||c_student%rowcount||
 '行');
 
     
 else
 
       exit;
 
     
 end if;  
 
   
 end loop;
 
   
 close c_student;
 
exception
 
   
 when others 
  then
 
     if c_student%isopen 
 then
 
       
 close c_student;
 
     
 end if;    
 
end;
 
-
 -使用游标
 for循环处理游标
 
-
 -它简化了游标的处理过程。
 
-
 -借助于游标
 FOR循环,游标打开、检索和关闭的过程
 
-
 -被隐含地自动实现。这使得语句块代码更容易编写和维护
 
-
 -语法:
 
for  record_variable_name 
  in  
 cursor_name   loop
 
       
 statement;
 
end loop;
 
-
 -该循环的每一次迭代,都会自动地从游标
 cursor_name 
 
-
 -中检索出一行记录,并把它存放到记录变量
 
-
 - record_variable_name中。在循环体的语句中,
 
-
 -使用句点(.)的语法,可通过变
 
-
 -量 record_variable_name来得到该行记录每列的值
 
-
 -示例
 11:
 
create  
 table  table_log
 
                (description  varchar2(
 250));
 
declare
 
   
 cursor c_student 
  is
 
     
 select student_id,last_name
 
       
 from student
 
       
 where student_id
 <
 110;
 
begin
 
   
 -
 -处理游标
 
   
 for r_student 
  in c_student loop
 
     
 insert 
  into table_log
 
       
 values(r_student.last_name);
 
   
 end loop;
 
end;
 
-
 -使用嵌套游标
 
-
 -实际上就是游标
 for循环的嵌套使用
 
-
 -示例
 13:
 
declare
 
   v_zip  zipcode.zip%
 TYPE;
 
   v_student_flag  
 CHAR;
 
   
 -
 -父游标.保存的是CT州的所有邮编
 
   
 cursor c_zip 
  is
 
     
 select zip,city,
 state
 
       
 from zipcode
 
       
 where 
  state
 =
 'CT';    
 
   
 -
 -子游标。保存特定邮编区域的学生姓名.
 
   
 -
 -注意:子游标的
 where条件中有个变量v_zip
 
   
 cursor c_student 
  is
 
     
 select first_name,last_name
 
       
 from student
 
       
 where zip
 =v_zip;  
 
begin
 
   
 -
 -遍历父游标
 
   
 for r_zip 
  in c_zip loop
 
     
 -
 -值为N表示该邮编区域没有学生
 
     v_student_flag :
 = 
  'N';
 
     
 -
 -给v_zip初始化以便打开子游标
 
     v_zip :
 = r_zip.zip; 
 
     
 -
 -输出一个空白行:
 
     dbms_output.put_line(chr(
 10)); 
 
     dbms_output.put_line(
 '学生住在:'||r_zip.city); 
 
     
 -
 -遍历子游标,输出学生的姓名
 
     
 for r_student 
  in c_student loop
 
       dbms_output.put_line(r_student.last_name);
 
       v_student_flag :
 = 
  'Y';
 
     
 end loop;
 
     
 -
 -如果该邮编区域没有学生
 
     if v_student_flag 
 = 
  'N' 
 then
 
       dbms_output.put_line(
 '没有学生');
 
     
 end if;
 
   
 end loop;
 
end;
 
-
 -使用集合(数组)
 
-
 -集合是相同类型元素的组合。在集合中,
 
-
 -使用唯一的下标来标识每个元素
 
-
 -一共有
 3种类型的集合:
 
-
 -索引表、嵌套表和变长数组
 
-
 -PL
 /
 SQL表有两种类型:索引表(或者叫做关联数组,
 
-
 -associative 
 array )和嵌套表(nested 
  table)
 
-
 -使用集合,主要为了使用一个或者几个变量就可以
 
-
 -保存大量的数据
 
-
 -
 1、使用索引表(相关数组)集合
 
-
 -示例
 1:分别声明一个游标和一个索引表类型,
 
-
 -游标从student表中检索出前
 10个学生的姓名。
 
-
 -遍历游标,将每个学生的姓名保存到一个索引表类型的
 
-
 -集合中,然后从集合中取出每个学生的姓名打印到屏幕上
 
declare
 
   
 -
 -声明游标
 
   
 cursor c_student 
  is
 
     
 select last_name
 
       
 from student
 
       
 where rownum
 <
 =
 10;
 
   
 -
 -声明索引表集合类型
 
   
 type last_name_type 
  is 
 table 
 of student.last_name%
 type 
 
     
 index 
  by pls_integer;
 
   
 -
 -声明集合变量
 
   last_name_tab last_name_type;
 
   v_index pls_integer :
 = 
  0;
 
begin
 
   
 -
 -遍历游标,将学生姓名保存到集合中
 
   
 for r_student 
  in c_student loop
 
      
 -
 -下标加
 1
 
      v_index :
 = v_index
 +
 1;
 
      
 -
 -保存
 
      last_name_tab(v_index) :
 = r_student.last_name;
 
   
 end loop;
 
   
 -
 -遍历集合
 
   
 for i 
  in 
 1..
 10 loop
 
      dbms_output.put_line(last_name_tab(i));
 
   
 end loop;
 
end;
 
-
 -
 2、使用嵌套表集合
 
-
 -创建嵌套表的语法如下所示(方括号中的内容可选的): 
 
   
 type type_name 
  is 
 table 
 of element_type [
 not 
 null]; 
 
   
 table_name  type_name;
 
-
 -注意,该声明非常类似于索引表的声明,只是没有
 INDEX 
 BY  子句。嵌套表的下标类型固定为
 Integer整型
 
-
 -修改示例
 1,将集合类型改为嵌套表类型
 
declare
 
   
 -
 -声明游标
 
   
 cursor c_student 
  is
 
     
 select last_name
 
       
 from student
 
       
 where rownum
 <
 =
 10;
 
   
 -
 -声明嵌套表集合类型
 
   
 type last_name_type 
  is 
 table 
 of student.last_name%
 type 
 
     ;
 
   
 -
 -声明集合变量
 
   last_name_tab last_name_type;
 
   v_index pls_integer :
 = 
  0;
 
begin
 
   
 -
 -遍历游标,将学生姓名保存到集合中
 
   
 for r_student 
  in c_student loop
 
      
 -
 -下标加
 1
 
      v_index :
 = v_index
 +
 1;
 
      
 -
 -保存
 
      last_name_tab(v_index) :
 = r_student.last_name;
 
   
 end loop;
 
   
 -
 -遍历集合
 
   
 for i 
  in 
 1..
 10 loop
 
      dbms_output.put_line(last_name_tab(i));
 
   
 end loop;
 
end;  
 
-
 -执行以上代码,抛出以下异常:
 
-
 -ORA
 -
 06531: Reference 
 to uninitialized collection
 
-
 -ORA
 -
 06512: 
 at line 
  24
 
-
 -必须使用嵌套表的构造器函数对嵌套表集合变量初始化。
 
-
 -改写代码:
 
declare
 
   
 -
 -声明游标
 
   
 cursor c_student 
  is
 
     
 select last_name
 
       
 from student
 
       
 where rownum
 <
 =
 10;
 
   
 -
 -声明嵌套表集合类型
 
   
 type last_name_type 
  is 
 table 
 of student.last_name%
 type 
 
     ;
 
   
 -
 -声明集合变量。必须使用构造器函数初始化
 
   last_name_tab last_name_type :
 = last_name_type();
 
   v_index pls_integer :
 = 
  0;
 
begin
 
   
 -
 -遍历游标,将学生姓名保存到集合中
 
   
 for r_student 
  in c_student loop
 
      
 -
 -下标加
 1
 
      v_index :
 = v_index
 +
 1;
 
      
 -
 -保存
 
      last_name_tab(v_index) :
 = r_student.last_name;
 
   
 end loop;
 
   
 -
 -遍历集合
 
   
 for i 
  in 
 1..
 10 loop
 
      dbms_output.put_line(last_name_tab(i));
 
   
 end loop;
 
end;  
 
-
 -执行以上代码,抛出以下异常:
 
-
 -ORA
 -
 06533: Subscript beyond 
 count
 
-
 -ORA
 -
 06512: 
 at line 
  24
 
-
 -为了把元素放到嵌套表集合中,必须首先调用
 
-
 -集合的
 extend方法给集合添加存储空间
 
-
 -改写代码:
 
declare
 
   
 -
 -声明游标
 
   
 cursor c_student 
  is
 
     
 select last_name
 
       
 from student
 
       
 where rownum
 <
 =
 10;
 
   
 -
 -声明嵌套表集合类型
 
   
 type last_name_type 
  is 
 table 
 of student.last_name%
 type 
 
     ;
 
   
 -
 -声明集合变量。必须使用构造器函数初始化
 
   last_name_tab last_name_type :
 = last_name_type();
 
   v_index pls_integer :
 = 
  0;
 
begin
 
   
 -
 -遍历游标,将学生姓名保存到集合中
 
   
 for r_student 
  in c_student loop
 
      
 -
 -下标加
 1
 
      v_index :
 = v_index
 +
 1;
 
      
 -
 -首先给集合添加存储空间
 
      last_name_tab.
 extend;
 
      
 -
 -保存
 
      last_name_tab(v_index) :
 = r_student.last_name;
 
   
 end loop;
 
   
 -
 -遍历集合
 
   
 for i 
  in 
 1..
 10 loop
 
      dbms_output.put_line(last_name_tab(i));
 
   
 end loop;
 
end;  
 
-
 -集合方法:
 
-
 -
 count、
 first、
 last、
 prior、
 next、
 delete、
 trim、
 exists
 
-
 -
 3、使用变长数组集合类型
 
-
 -创建变长数组的语法如下所示 
 
TYPE  type_name 
  IS  VARRAY(siz e_limit)  
 OF  element_type; 
 
 varray_name  TYPE_NAME;
 
-
 -变长数组有最大元素数量限制。
 
-
 -他在使用上和嵌套表的语法限制是完全一样的:
 
-
 -
 1)必须对变长数组进行初始化
 
-
 -
 2)在放元素到变长数组集合中之前,必须调用
 extend方法
 
-
 -添加存储空间
 
declare
 
   
 -
 -声明游标
 
   
 cursor c_student 
  is
 
     
 select last_name
 
       
 from student
 
       
 where rownum
 <
 =
 10;
 
   
 -
 -声明变长数组集合类型
 
   
 type last_name_type 
  is varray(
 10) 
  of student.last_name%
 type 
 
     ;
 
   
 -
 -声明集合变量。必须使用构造器函数初始化
 
   last_name_tab last_name_type :
 = last_name_type();
 
   v_index pls_integer :
 = 
  0;
 
begin
 
   
 -
 -遍历游标,将学生姓名保存到集合中
 
   
 for r_student 
  in c_student loop
 
      
 -
 -下标加
 1
 
      v_index :
 = v_index
 +
 1;
 
      
 -
 -首先给集合添加存储空间
 
      last_name_tab.
 extend;
 
      
 -
 -保存
 
      last_name_tab(v_index) :
 = r_student.last_name;
 
   
 end loop;
 
   
 -
 -遍历集合
 
   
 for i 
  in 
 1..
 10 loop
 
      dbms_output.put_line(last_name_tab(i));
 
   
 end loop;
 
end;  
 
-
 -注意:不能对变长数组使用
 delete方法来删除其元素,
 
-
 -使用
 delete方法会导致错误:
 
-
 -多层集合
 
-
 -如果一个集合的元素又是一个集合,那么
 
-
 -该集合就叫做多层集合。
 
-
 -多层集合对应于其它语言中的二维数组
 
-
 -所谓二维数组,就是有一个一维数组,其中的
 
-
 -每个元素又是一个一维数组。
 
-
 -要取出二维数组中的每个元素,必须使用两个下标。
 
declare
 
   
 type varray_type1 
  is varray(
 4) 
  of pls_integer;
 
   
 type varray_type2 
  is varray(
 3) 
  of varray_type1;
 
   varray1 varray_type1 :
 = varray_type1(
 2,
 4,
 6,
 8);
 
   
 -
 -二维数组
 
   varray2 varray_type2 :
 = varray_type2(varray1);
 
begin
 
   varray2.
 extend;
 
   
 -
 -给varray2再添加一个元素
 
   varray2(
 2) :
 = varray_type1(
 1,
 3,
 5,
 7);  
 
   
 -
 -遍历varray2数组
 
   
 for i 
  in 
 1..
 2 loop
 
     
 for j 
  in 
 1..
 4 loop
 
       dbms_output.put_line(varray2(i)(j));   
 
     
 end loop;
 
   
 end loop;
 
   
 -
 -灵活的写法
 
   
 for i 
  in varray2.
 first..varray2.
 last loop
 
     
 for j 
  in 
 1..varray2(i).
 count loop
 
       dbms_output.put_line(varray2(i)(j));   
 
     
 end loop;
 
   
 end loop;
 
end;










