oracle--13集合

Oracle——13集合

集合类型  

1. 使用条件:
   a. 单行单列的数据,使用标量变量 。
   b. 单行多列数据,使用记录 [ 详细讲解请见: 点击打开链接 ]
   c. 单列多行数据,使用集合
       *集合:类似于编程语言中数组也就是。pl/sql集合类型包括关联数组Associative array(索引表 pl/sql table)、嵌套表(Nested Table)、变长数组(VARRAY)。

2. 三种集合类型区别:
     Nested table与VARRY既可以被用于PL/SQL,也可以被直接用于数据库中,但是Associative array不行,也就是说,Associative array是不能通过CREATE TYPE语句进行单独创建,只能在PL/SQL块(或Package)中进行定义并使用(即适用范围是PL/SQL Block级别),而Nested table与VARRAY则可以使用CREATE TYPE进行创建(即适用范围是Schema级别),它们还可以直接作为数据库表中列的类型。

3.  集合的方法:
       exists(index) 索引处的元素是否存在  
       count 当前集合中的元素总个数  
       limit 集合元素索引的最大值  
          索引表和嵌套表是不限个数的,所以返回null,变长数组返回定义时的最大索引  
       first  返回集合第一个元素索引  
       last  返回集合最后一个元素索引  
       prior 当前元素的前一个  
       next 当前元素的后一个  
      
       extend 扩展集合的容量,增加元素 只是用于嵌套表和varry类型  
           x.extend 增加一个null元素  
           x.extend(n) 增加n个null元素  
           x.extend(n,i) 增加n个元素,元素值与第i个元素相同  
       trim 从集合的尾部删除元素 只用于NEST TABLE和VARRY类型  
       trim 从集合尾部删除一个元素  
       trim(n) 从集合尾部删除n个元素  
       delete 按索引删除集合元素  
           delete 删除所有  
           delete(index) 删除第index个  
           delete(a,b) 删除a--b之间的所有元素

4.  集合类型的声明与初始化:

    1)关联数组:
         a. 下标无限制,可以为负数  
         b. 元素个数无限制  
         c.  定义  
              TYPE type_name IS TABLE OF element_type [NOT NULL] INDEX BY key_type;  
              type_name:用户自定义数据类型的名字  
              element_type:索引表中元素类型  
              key_type:索引表元素下标的数据类型(BINARY_INTEGER,PLS_INTEGER,VARCHAR2)  
         d. 例1:
            
declare
  type index_tab_type is table of varchar2(30) index by BINARY_INTEGER;
  v_table index_tab_type;
begin
  v_table(-1) :='hello';--设定下标为-1的元素的值
  v_table(1)  :=',';
  dbms_output.put_line(v_table(-1)||'-'||v_table(1));
  dbms_output.put_line('元素个数:'||v_table.count);
  v_table(5) :='world';
  dbms_output.put_line('元素个数:'||v_table.count);
  dbms_output.put_line('第一个元素'||v_table.first);
  dbms_output.put_line('最后一个元素'||v_table.last);
end;
/

hello-,
元素个数:2
元素个数:3
第一个元素-1
最后一个元素5

PL/SQL 过程已成功完成。
         e.例2:使用varchar2作为索引元素类型 ,其实也就和java中的map一样了 key-value(键值对)形式存储
declare
  type index_tab_type is table of varchar2(30) index by varchar2(30);
  v_table index_tab_type;
  v_record emp%rowtype;
begin
  --emp表中查询3条记录,以name-job的形式存储到索引表中
  select * into v_record from emp where emp.empno=7788;
  v_table(v_record.ename):= v_record.job;
  select * into v_record from emp where emp.empno=7844;
  v_table(v_record.ename):= v_record.job;
  select * into v_record from emp where emp.empno=7900;
  v_table(v_record.ename):= v_record.job;
  dbms_output.put_line(v_table.count);--3
  dbms_output.put_line(v_table(v_record.ename));--CLERK
end;
/
3
CLERK

PL/SQL 过程已成功完成。


     2)嵌套表 Nested Table
         a. 下标从1开始,元素个数灭有限制(*使用时必须先初始化,用extend属性可以扩展元素个数)  
         b.  可以作为表定义数据类型,但是前提是要先create 创造嵌套表类型,这就可以实现1对多了定义  
                TYPE type_name IS TABLE OF element_type;  
         c.  和索引表的区别也就是看看有无index by语句,嵌套表的索引固定是int型的 . 
         d.例1:
declare
     type nest_table_type is table of emp.ename%type;
     v_nest_tab nest_table_type;
begin
     v_nest_tab :=nest_table_type('x');--初始化 必须! 语句 type_name(...)
     select ename into v_nest_tab(1) from emp where empno=7788;
     dbms_output.put_line(v_nest_tab(1));
end;

SCOTT

PL/SQL 过程已成功完成。

          e.例2:在表列中使用嵌套表 嵌套表类型的列是单独一个表存储,先创建一个这样的类型才能使用
create type nest_tab_type is table of varchar2(30);  
  create table test_nest_tab( 
     id int, 
     vals nest_tab_type --使用 
  ) nested table vals store as nest_tab;--vals字段用嵌套表存储,表明nest_tab 
  --上面语句执行完之后,在生成TEST_NEST_TAB的同时会生出一个关联表NEST_TAB用来存储关联表的数据 
--插入数据 
insert into test_nest_tab values(1,nest_tab_type('one','two','three','four')); 
--查询数据 
declare  
  v_id int; 
  v_tab nest_tab_type; 
begin 
  select * into v_id,v_tab from test_nest_tab where id=1; 
  dbms_output.put_line(v_id); 
  for i in 1..v_tab.count loop 
  dbms_output.put_line(v_tab(i)); 
  end loop; 
end; 

1
one
two
three
four

PL/SQL 过程已成功完成。

      3)Varry 可变数组
        a. 定义  
             TYPE type_name IS VARRAY(size_limit) OF element_type[NOT NULL];  
        b.  这个就和java中的数组差不多了,下标from 1 ,定义时先指定最大元素个数,也和varchar2(size)这种一样。  
        c.  使用时也必须先用构造方法初始化 ,可以作为表列类型 
        d.  例1:
       
declare
   type varr is VARRAY(10) of int;
   v_varr varr :=varr();
begin
   --dbms_output.put_line(varr.count);
   for i in 1..5 loop
       v_varr.extend;
       v_varr(i) :=i*i;
   end loop;

   for i in 1..5 loop
       dbms_output.put_line(v_varr(i));
   end loop;
end;
/

1
4
9
16
25

PL/SQL 过程已成功完成。

            e.例2:可变数组作为表列类型 可变数组是存储在表内部的,不同于嵌套表
create type varr_type is varray(10) of varchar2(30);--先创建类型 
create table test_varray( 
    id int, 
    name varchar2(30), 
    params varr_type --param是使用可变数组类型 
); 
--插入数据 
insert into test_varray values(1,'bird',varr_type('a','b','c')); 
--查询数据 
declare  
    v_varr varr_type; 
    v_name test_varray.name%type; 
begin 
    select name,params into v_name,v_varr from test_varray where id=1; 
    for i in 1..v_varr.count loop 
    dbms_output.put_line(v_varr(i)); 
    end loop; 
end;


a
b
c

PL/SQL 过程已成功完成。


5. 如何选择适用的集合类型:

       通常来说,对集合类型的第一选择应该是Associative array,因为它不需要初始化或者EXTEND操作,并且是迄今为止最高效的集合类型。唯一不足的一点是它只能用于PL/SQL而不能直接用于数据库。
  如果你需要允许使用负数索引,应该选择Associative array;
  如果你需要使用10g,11g中的那些集合操作,应该选择Nested table;
  如果你需要限制集合元素的个数,应该选择VARRAY。

集合
Oracle中一共有三种集合类型:index-by表、嵌套表和可变数组。其中index-by表只存在于PL/SQL中,而不能直 接存储在数据库表中。嵌套表可以存储在数据库表中。index-by表和嵌套表统称为PL/SQL表。可变数组被声明为具有固定数目的元素,而PL /SQL表没有声明上限。
一、index-by表
  index-by表在句法上类似于C或JAVA中的数组。首先需要定义表的属性,然后在声明使用。
  句法:TYPE   tabletype  IS  TABLE  OF  type  INDEX BY BINARY_INTEGER;
  其中tabletype是所定义的新类型的类型名,type是一个预定义的类型,或通过%TYPE或%ROWTYPE对一个类型的引用。INDEX BY BINARY_INTEGER子句是该表定义的一部分。这
个子句对于嵌套表是不存在的。
  一旦声明了类型和变量,就可以引用其中的单个元素:tablename(index)
  其中tablename是表的名称,index是一个BINARY_INTEGER类型的变量,或者是一个可以转换成BINARY_INTEGER类型的变量或表达式。
注意:
  a、index-by表是无约束的。其行数的唯一限制(除可用内存外)就是它的关键字是BINARY_INTEGER类型的,因此要受到BINARY_INTEGER类型所能表示数值的约束
(-2147483647~2147483647)。
  b、index-by表中的元素不一定要按任何特定顺序排列。因为它们不像数组那样在内存中是连续存储的,所以其元素可以借助于任意关键字而插入(如果你从PL/SQL中把一个index-by
表传递到C或JAVA的主机数组,其元素应该从1开始依次编号)。
  c、用于index-by表的关键字没有必要是有顺序的。任何BINARY_INTEGER值或表达式都可以用作表的索引。
  d、关键字唯一允许的类型是BINARY_INTEGER。
  e、index-by表类似于数据库表,它有key和value两列。key的类型是BINARY_INTEGER,而value的类型是在定义中指定的任何数据类型,可以是简单数值,也可以是记录,或者是对
象,也可以是集合。如果value的值是一条条记录,那么可以是用table(index).field来引用该记录的字段。
  f、对于index-by表中的元素i赋值时,如果该元素i不存在,实际上会创建一个元素i,这类似于对数据库表进行的INSERT操作。如果对元素i引用,而其不存在,那么会抛出一个异常。可
以使用DELETE方法来删除表元素。
例如:
DECLARE
   TYPE NameTab IS TABLE OF students.first_name%TYPE INDEX BY BINARY_INTEGER;
   TYPE DateTab IS TABLE OF DATE INDEX BY BINARY_INTEGER;
   v_Names NameTab;
   v_Dates DateTab;
BEGIN
   v_Names(1) := 'Scott';
   v_Dates(-4) := SYSDATE - 1;
END;
非标量类型的index-by表
DECLARE
   TYPE StudentTab IS TABLE OF students%ROWTYPE INDEX BY BINARY_INTEGER;
   v_Students StudentTab;
BEGIN
  SELECT *
   INTO v_Students(10001)
   FROM students
  WHERE id=10001;
 v_Students(1).first_name := 'Larry';
 v_Students(1).last_name := 'Lemon';
END;
对象类型的index-by表
CREATE OR REPLACE TYPE MyObject AS OBJECT(
   field1 NUMBER,
   field2 VARCHAR2(20),
   field3 DATE);
DECLARE
   TYPE ObjectTab IS TABLE OF MyObject INDEX BY BINARY_INTEGER;
   v_Objects ObjectTab;
BEGIN
  v_Objects(1) := MyObject(1,null,null);
  v_Objects(1).field2 := 'Hello World!';
  v_Objects(1).field3 := SYSDATE;
END;
二、嵌套表
  嵌套表的基本功能与index-by表相同,嵌套表可以被看做是具有两个列的数据库表。我们可以从嵌套表中删除元素,这样得到一个具有非有序关键字的稀疏表,这个表就像index-by表
。然而,嵌套表必须用有序的关键字创建,而且关键字不能是负数。此外,嵌套表可以存储到数据库中,而index-by表则不能。嵌套表中的最大行数是2G字节,这也是最大的关键字值。
  创建一个嵌套表类型的句法:TYPE table_name IS TABLE OF table_type [NOT NULL];
  其中table_name是新类型的类型名字,table_type是嵌套表中每一个元素的类型,它可以是用户定义的对象类型,也可以是使用%TYPE的表达式,但是它不可以是BOOLEAN、
NCHAR、NCLOB、NVARCHAR2或REF CURSOR。如果存在NOT NULL,那么嵌套表的元素不能是NULL。
  当声明了一个嵌套表时,它还没有任何元素,它会被自动初始化为NULL。此时如果直接使用它的话就会抛出一个COLLECTION_IS_NULL的异常。可以使用构造器来进行初始化,嵌
套 表的构造器与表的类型名本身具有相同的名称。然而,构造器有不定数目的参数,每一个参数都应该与表元素类型可以兼容,每一个参数就是其中的表元素。参数成 为从索引1开始有序的表元素。如果使用的是不带参数的构造器进行初始化,这会创建一个没有元素的空表,但不会被初始化为NULL。虽然表是无约束的,但是 你不能对不存在的元素赋值,这样将会导致表的大小增加,会抛出一个异常。你可以使用EXTEND方法来增加嵌套表的大小。
例如:
DECLARE
  TYPE NumbersTab IS TABLE OF NUMBER;
  v_Tab1 NumbersTab := NumbersTab(-1);
  v_Primes NumbersTab := NumbersTab(1,2,3,5,7);
  v_Tab2 NumbersTab := NumbersTab();
BEGIN
  v_Tab1(1) := 12345;

  FOR v_Count IN 1..5 LOOP
   DBMS_OUTPUT.PUT(v_Primes(v_Count) || ' ');
  END LOOP;
  DBMS_OUTPUT.NEW_LINE;
END;
三、可变数组
  可变数组是一种非常类似于C或JAVA数组的数据类型。对可变数组的访问与对嵌套表或index-by表的访问类似。但是,可变数组在大小方面有一个固定的上界,这个上界作为类型声明
的一部分被指定。可变数组不是没有上界的稀疏数据结构,元素插入可变数组中时以索引1开始,一直到在可变数组类型中声明的最大长度。可变数组的极限大小也是2G字节。可变数组的元素在内存中连续存储。这不同于嵌套表的存储,嵌套表更像一个数据库表。
  可变数组类型声明的句法:TYPE type_name IS {VARRAY | VARYING ARRAY} (maximum_size) OF element_type [NOT NULL];
  其中type_name是新可变数组类型的类型名,maximum_size是一个指定可变数组中元素最大数目的一个整数,element_type是一个PL/SQL标量、记录或对象类型。element_type
可 以使用%TYPE来指定,但是它不可以是BOOLEAN、NCHAR、NCLOB、NVARCHAR2或REF CURSOR。可变数组也使用一个构造器来进行初始化。传递到构造器的参数数目成为可变数组的初始长度,它必须少于或等于在可变数组类型中指定的最大长 度。传递的参数就是可变数组的元素。如果引用可变数组的元素超出其界限,那么就会抛出SUBSCRIPT_OUTSIDE_LIMIT异常。可变数组的大 小也可以使用EXTEND方法来增加。不同于嵌套表的是,可变数组不能够被扩展超过为可变数组类型声明的极限大小。
例如:
DECLARE
  TYPE Numbers IS VARRAY(20) OF NUMBER(3);
  v_NullList Numbers;
  v_List1 Numbers := Numbers(1,2);
  v_List2 Numbers := Numbers(null);
BEGIN
  IF v_NullList IS NULL THEN
    DBMS_OUTPUT.PUT_LINE(' v_NullList is NULL');
  END IF;
  IF v_List2(1) IS NULL THEN
    DBMS_OUTPUT.PUT_LINE(' v_List2(1) is NULL');
  END IF;
END;
四、多层集合
  多层集合就是集合的集合,多层集合的类型声明与一维集合的声明相同,只是集合类型本身就是一个集合。因此我们使用两个括号来访问多层集合包含的元素。
例如:
DECLARE
  TYPE t_Numbers IS TABLE OF NUMBER INDEX BY BINARY_INTEGER;
  TYPE t_MultiNumbers IS TABLE OF t_Numbers INDEX BY BINARY_INTEGER;
  TYPE t_MultiVarray IS VARRAY(10) OF t_Numbers;
  TYPE t_MultiNested IS TABLE OF t_Numbers;
  v_MultiNumbers t_MultiNumbers;
BEGIN
  v_MultiNumbers(1)(1) := 12345;
END;
五、三种集合之间的比较
  1、可变数组与嵌套表
  两者的相似之处:
  ●两种类型(加上index-by表)都使用PL/SQL中的下标符号来允许对单个元素的访问。
  ●两种类型都可以存储在数据库表中(当在PL/SQL语句块外声明时)。
  ●集合方法可以应用于这两种类型。
  两者的区别:
  ●可变数组有大小上限,而嵌套表没有一个明确的大小上限。
  ●当存储到数据库中时,可变数组保持了元素的排序和下标的值,而嵌套表却不同。
  2、嵌套表和index-by表
  两者的相似之处:
  ●两种表的数据类型具有相同的结构。
  ●两种表中的单个元素都是使用下标符号进行访问的。
  ●嵌套表可用的方法包括index-by表的所有表属性。
  两者的区别:
  ●嵌套表可以使用SQL进行操作,而且可以存储到数据库中,而index-by表则不能。
  ●嵌套表合法的下标范围为1..2147483647,而index-by的范围为-2147483647..2147483647。index-by表可以有负数下标,而嵌套表则不能。
  ●嵌套表可以自动为NULL(用IS NULL操作符检验)。
  ●要添加元素,必须初始化和扩展嵌套表。
  ●嵌套表有可用的其它方法,如EXTEND和TRIM。
  ●PL/SQL会自动在主机数组和index-by表之间进行转换,但不能在主机数组和嵌套表之间转换。
六、数据库中的集合
1、存储集合的隐含式
a、模式层类型
  为了从数据库表中存储和检索一个集合,该集合类型必须为PL/SQL和SQL所知。这意味着它不能是PL/SQL的局部集合,而应该与对象类型类似,使用CREATE TYPE语句来声明。
在 模式层创建的类型对于PL/SQL来说是全局的,它有类似于任何其他数据库对象的范围和可见性规则。模式层的类型也可以用作数据库的列。声明为PL /SQL语句块的局部类型,只在声明它的那个语句块中可见,而且不可用作数据库的列。在包头声明的类型在整个PL/SQL中都是可见的,也仍然不能用作数 据库的列。只有模式层的类型才可以用作数据库的列。
例如:
SQL>CREATE OR REPLACE TYPE NameList AS VARRAY(20) OF VARCHAR2(30);
   /

DECLARE
  TYPE DateList IS VARRAY(10) OF DATE;
  v_Dates DateList;
  v_Names NameList;
BEGIN
  NULL;
END;
b、已存储可变数组的结构
  可变数组可以用作数据库列的类型。在这种情况下,整个可变数组都与其他列并排着被存储到一个数据库行中。不同的行包含不同的可变数组。
注意:大于4K的可变数组数据实际上将与其余的表列分开存储,它将存储到LOB中。任何可变数组列的类型必须为数据库所知,并被存储在数据字典中,因此需要有CREATE TYPE语句。
例如:
CREATE OR REPLACE TYPE BookList AS VARRAY(10) OF NUMBER(4);
CREATE TABLE class_material(
  department    CHAR(3),
  course      NUMBER(3),
  required_reading BooList
);
c、已存储嵌套表的结构
  嵌套表也可以被存储为数据库的一个列。数据库表的每一行都可以包含一个不同的嵌套表。
注意:●表的类型在表定义中使用,就像列对象或内置类型那样。它必须是以CREATE TYPE语句创建的模式层类型。
    ●对于每一个给定数据库表中的嵌套表,都需要NESTED TABLE子句。这个子句表明了存储表的名称。
    ●存储表是系统产生的表,用来存储嵌套表中的实际数据。与已存储可变数组不同,嵌套表的数据并不是存储在表列中而是单独存储的。
    ●存储表可以以另一种模式存在,而且可以有与主表不同的存储参数。存储表可以在user_tables中描述并存在于其中,但是不能被直接访问。
例如:
CREATE OR REPLACE TYPE StudentList AS TABLE OF NUMBER(5);
CREATE TABLE library_catalog(
 catalog_number  NUMBER(4),
   FOREIGN KEY (catalog_number) REFERENCES books(catalog_number),
 num_copies       NUMBER,
 num_out     NUMBER,
 checked_out    StudentList)
NESTED TABLE checked_out STORE AS co_tab;
2、操作整个集合
  你可以使用SQL DML 语句来操作一个存储集合。这些类型的操作将影响到整个集合,而非单个元素。集合中的元素可以通过使用PL/SQL操作,也可以通过SQL运算符操作。
a、INSERT
  INSERT语句被用来将集合插入到数据库行中,必须首先创建并初始化一个集合。
b、UPDATE
  UPDATE也被用来修改一个存储集合。
c、DELETE
  DELETE可以删除一个包含集合的行。
例如:
DECLARE
  v_StudentList  StudentList := StudentList(10000,10001,10002);
  
BEGIN
  INSERT INTO library_catalog(catalog_number,num_copies,num_out)
  VALUES(1000,20,3);
  INSERT INTO library_catalog(catalog_number,num_copies,num_out)
  VALUES(1001,20,3);
  INSERT INTO library_catalog(catalog_number,num_copies,num_out)
  VALUES(1000,20,3);
  INSERT INTO library_catalog(catalog_number,num_copies,num_out)
  VALUES(1002,20,3);

  UPDATE library_catalog
  SET  checked_out = v_StudentList
  WHERE catalog_number = 1000;

  DELETE FROM library_catalog
  WHERE catalog_number = 1000;
END;
d、SELECT
  使用SELECT语句可把集合从数据库检索到PL/SQL变量中。一旦集合保存到PL/SQL中,就可以使用过程化语句来操作它。
查询可变数组:
例如:
CREATE OR REPLACE PROCEDURE PrintRequired(
  p_Department  IN  class_material.department%TYPE,
  p_Course  IN  class_material.course%TYPE)
IS
  v_Books class_material.required_reading%TYPE;
  v_Title books.title%TYPE;
BEGIN
  SELECT required_reading
    INTO v_Books
   FROM class_material
  WHERE department = p_Department
     AND  course = p_Course;
 
  FOR v_Index IN 1..v_Books.COUNT LOOP
   SELECT title
      INTO v_Title
    FROM books
   WHERE catalog_number = v_Books(v_Index);
  END LOOP;
END;
查询嵌套表:
  当一个嵌套表被检索进某PL/SQL变量时,它就被赋于从1开始一直排到表中元素数的关键字值。后者可以使用COUNT方法来决定。
例如:
CREATE OR REPLACE PACKAGE BODY Library AS
  PROCEDURE PrintCheckedOut(p_CatalogNumber  IN  library_catalog.catalog_number%TYPE) IS
  v_StudentList  StudentList;
  v_Student students%ROWTYPE;
  v_Book books%ROWTYPE;
  v_FoundOne  BOOLEAN := FALSE;
BEGIN
  SELECT checked_out
     INTO  v_StudentList
   FROM  library_catalog
  WHERE  catalog_number = p_CatalogNumber;

  IF v_StudentList IS NOT NULL THEN
   FOR v_Index IN 1..v_StudentList.COUNT LOOP
     v_FoundOne := TRUE;

     SELECT *
        INTO v_Student
       FROM Students
      WHERE ID = v_StudentList(v_Index);
   END LOOP;
  END IF;
END;
带有无序关键字的已存储表:
  存储在数据库中的嵌套表不能用PL/SQL直接操作,而只能用SQL操作。因此,关键字值并不被记录下来。当使用SELECT命令从数据库中选择一个嵌套表时,关键字从1开始依次重
新编号。因而,如果你把一个带有无序关键字的嵌套表插入到数据库时,关键字值将会改变,关键字也会从1开始依次重新编号。
例如:
CREATE OR REPLACE TYPE DateTab AS TABLE OF DATE;
CREATE TABLE famous_dates(
 key    VARCHAR2(100) PRIMARY KEY;
 date_list  DateTab)
NESTED TABLE date_list STORE AS dates_tab;
/
DECLARE
  v_Dates DateTab := DateTab(TO_DATE('04-JUL-1776','DD-MON-YYYY'),
                 TO_DATE('12-APR-1861','DD-MON-YYYY'),
                 TO_DATE('05-JUN-1968','DD-MON-YYYY'),
                 TO_DATE('26-JAN-1986','DD-MON-YYYY'),
                 TO_DATE('01-JAN-2001','DD-MON-YYYY'));
  PROCEDURE Print(p_Dates IN DateTab) IS
     v_Index BINARY_INTEGER := p_Dates.FIRST;
  BEGIN
    WHILE v_Index <= p_Dates.LAST LOOP
      DBMS_OUTPUT.PUT(' ' || v_Index || ': ' );
      DBMS_OUTPUT.PUT_LINE(TO_CHAR(p_Dates(v_Index), 'DD-MON-YYYY'));
      v_Index := p_Dates.NEXT(v_Index);
    END LOOP;
  END Print;
BEGIN
  v_Dates.DELETE(2);
  DBMS_OUTPUT.PUT_LINE(' Initial value of the table: ');
  Print(v_Dates);

  INSERT INTO famous_dates(key, date_list)
  VALUES('Dates in American History', v_Dates);

  SELECT date_list
        INTO v_Dates
      FROM famous_dates
  WHERE key = 'Dates in American History';

  DBMS_OUTPUT.PUT_LINE('Table after INSERT and SELECT : ');
  Print(v_Dates);
END;
3、操作单个集合元素
a、PL/SQL操作
  把一个集合元素选入到PL/SQL变量中,然后通过操作这个变量来操作单个集合元素。
b、SQL表操作符
  可以直接使用带TABLE操作符的SQL来操作已存储嵌套表的元素,操作它并把它更新回数据库。然而,已存储可变数组的元素不能直接用DML操作,它们必须在PL/SQL中操作。
  语法:TABLE(subquery)
  其中subquery是一个返回嵌套表列的查询。
例如:
PROCEDURE PrintCheckedOut(p_CatalogNumber IN library_catalog.catalog_number%TYPE) IS
  v_StudentList  StudentList;
  v_Student students%ROWTYPE;
  v_Book   books%ROWTYPE;
  v_FoundOne  BOOLEAN := FALSE;
  CURSOR c_CheckedOut  IS
    SELECT  column_value ID
        FROM  TABLE(SELECT checked_Out
                  FROM  library_catalog
            WHERE catalog_number = p_CatalogNumber);
BEGIN
  SELECT *
     INTO v_Book
   FROM books
  WHERE catalog_number = p_catalogNumber;

  FOR v_Rec IN c_CheckedOut  LOOP
    v_FoundOne := TRUE;

    SELECT *
       INTO v_Student
     FROM students
    WHERE ID = v_Rec.ID;

  END LOOP;
END PrintCheckedOut;

查询已存储可变数组
  虽然已存储可变数组的元素不能用DML语句操作(不同于嵌套表),但是可变数组可以使用TABLE操作符进行查询。在这种情况下,TABLE接受一个可变数组列,并返回其中的元素,
好像可变数组本身是一个单独的单列表。该列的列名是column_value。
例如:
SELECT department, course, column_value
 FROM class_material, TABLE(required_reading);
七、集合方法
  嵌套表和可变数组是对象类型,它们本身就有定义方法。index-by表有属性。
  属性或集合方法都使用下列语法调用:collection_instance.method_or_attribute
  其中,collection_instance是一个集合变量(不是类型名),method_or_attribute是一个方法或属性。这些方法只能从过程化语句调用,而不能从SQL语句调用。
1、EXISTS
  EXISTS被用来确定所引用的元素是否在集合中存在。
  语法:EXISTS(n)
  其中,n是一个整数表达式。如果由n指定的元素存在,即便该元素是NULL(无效)的,它也会返回TRUE。如果n超出了范围,EXISTS就返回FALSE,而不是抛出异常。EXISTS和
DELETE可以被用来维护稀疏嵌套表。EXISTS也可以应用于自动的NULL嵌套表或可变数组,在这些情况下,它总是返回FALSE。
例如:
CREATE OR REPLACE TYPE NumTab AS TABLE OF NUMBER;
CREATE OR REPLACE TYPE NumVar AS VARRAY(25) OF NUMBER;
CREATE OR REPLACE PACKAGE IndexBy AS
  TYPE NumTab IS TABLE OF NUMBER INDEX BY BINARY_INTEGER;
END IndexBy;

DECLARE
  v_NestedTable NumTab := NumTab(-7, 14.3, 3.14159, NULL, 0);
  v_Count  BINARY_INTEGER := 1;
  v_IndexByTable IndexBy.NumTab;
BEGIN
  LOOP
   IF v_NestedTable.EXISTS(v_Count) THEN
    DBMS_OUTPUT.PUT_LINE(' v_NestedTable(' || v_Count || '): ' || v_NestedTable(v_Count));
    v_Count := v_Count + 1;
   ELSE
    EXIT;
   END IF;
  END LOOP;

  v_IndexByTable(1) := -7;
  v_IndexByTable(2) := 14.3;
  v_IndexByTable(3) := 3.14159;
  v_IndexByTable(4) := NULL;
  v_IndexByTable(5) := 0;

  v_Count := 1;
  LOOP
   IF v_IndexByTable.EXISTS(v_Count) THEN
    DBMS_OUTPUT.PUT_LINE(' v_IndexByTable(' || v_Count || '): ' || v_IndexByTable(v_Count));
    v_Count := v_Count +1;
   ELSE
    EXIT;
   END IF;
  END LOOP;
END;
2、COUNT
  COUNT返回目前在集合中的元素数,它是一个整数。COUNT不带参数,而且在整数表达式有效处,它也有效。对于可变数组,COUNT总是与LAST相等,因为从可变数组中不能删除
元素。然而,从嵌套表中可以删除元素,因此,对于一个表来说,COUNT可能与LAST不同。当从数据库中选择一个嵌套表时,COUNT非常有用,因为那时元素的数目是未知的。在计算总
数时,COUNT将忽略已删除的元素。
3、LIMIT
  LIMIT返回一个集合目前的最大可允许的元素数(上限)。因为嵌套表没有大小上限,所以当应用于嵌套表时,LIMIT总是返回NULL。LIMIT对于index-by表是无效的。
例如:
DECLARE
  v_Table NumTab := NumTab(1,2,3);
  v_Varray NumVar := NumVar(1234,4321);
BEGIN
  DBMS_OUTPUT.PUT_LINE(' Varray limit : ' || v_Varray.LIMIT);
  DBMS_OUTPUT.PUT_LINE(' Varray count : ' || v_Varray.COUNT);
  IF v_Table.LIMIT IS NULL THEN
    DBMS_OUTPUT.PUT_LINE(' Table limit is NULL' );
  ELSE
    DBMS_OUTPUT.PUT_LINE('Table limit : ' || v_Table.LIMIT);
  END IF;

  DBMS_OUTPUT.PUT_LINE(' Table count : ' || v_Table.COUNT);
END;
4、FIRST和LAST
  FIRST返回集合中第一个元素的索引,而LAST返回最后一个元素的索引。对于可变数组,FIRST总是返回1,而LAST总是返回COUNT的值,这时因为可变数组是密集的,而且它的元
素不能被删除。FIRST和LAST可以与NEXT和PRIOR一起使用,来循环处理集合。
5、NEXT和PRIOR
  NEXT和PRIOR用来增加和减少集合的关键字值。
  它们的语法是:NEXT(n)和PRIOR(n)
  其中的n是一个整数表达式。NEXT(n)返回紧接在位置n处元素后面的那个元素的关键字,PRIOR(n)返回位置n处元素前面的那个元素的关键字。如果其前或其后没有元素,那么
NEXT和PRIOR将返回NULL。
例如:
DECLARE
  TYPE CharTab IS TABLE OF CHAR(1);
  v_Characters CharTab := CharTab('M' , 'a', 'd', 'a', 'm', ',', ' ', 'I', '''', 'm', ' ', 'A', 'd', 'a', 'm');
  v_Index INTEGER;
BEGIN
  v_Index := v_Characters.FIRST;
  WHILE v_Index <= v_Characters.LAST LOOP
   DBMS_OUTPUT.PUT(v_Characters(v_Index));
   v_Index := v_Characters.NEXT(v_Index);
  END LOOP;
  DBMS_OUTPUT.NEW_LINE;
 
  v_Index := v_Characters.LAST;
  WHILE v_Index >= v_Characters.FIRST LOOP
   DBMS_OUTPUT.PUT(v_Characters(v_Index));
   v_Index := v_Characters.PRIOR(v_Index);
  END LOOP;
  DBMS_OUTPUT.NEW_LINE;
END;
6、EXTEND
  EXTEND被用来把元素添加到嵌套表或可变数组的末端。它对于index-by表是无效的。
  EXTEND有三种形式:
  EXTEND
  EXTEND(n)
  EXTEND(n, i)
  没有参数的EXTEND仅仅用索引LAST+1把一个NULL元素添加到集合的末端。EXTEND(n)把n个NULL元素添加到表的末端,而EXTEND(n, i)把元素i的n个副本添加到表的末端。如
果该集合是用NOT NULL约束创建的,那么只有最后的这种可以使用,因为它不添加NULL元素。
  因为嵌套表没有一个明确的大小上限,所以你可以调用带n的EXTEND,n根据需要可取任意大的值(其大小上限是2G,同时受内存限制的影响)。然而,可变数组只能被扩展到其大小
上限,因此,n最大可以是(LIMIT - COUNT)。
  EXTEND对集合的内部大小进行操作,这包括嵌套表的任何已删除的元素。当一个元素已删除时(DELETE方法),该元素的数据也被消除,但是关键字却保留了下来。
例如:
DECLARE
  v_Numbers NumTab := NumTab(-2, -1, 0, 1, 2);
  PROCEDURE Print(p_Table IN NumTab) IS
    v_Index INTEGER;
  BEGIN
    v_Index := p_Table.FIRST;
    WHILE v_Index <= p_Table.LAST LOOP
      DBMS_OUTPUT.PUT(' Element ' || v_Index || ' : ‘);
      DBMS_OUTPUT.PUT_LINE(p_Table(v_Index));
      v_Index := p_Table.NEXT(v_Index);
    END LOOP
  END Print;
BEGIN
  DBMS_OUTPUT.PUT_LINE(' At initialization , v_Numbers contains');
  Print(v_Numbers);

  v_Numbers.DELETE(3);
  DBMS_OUTPUT.PUT_LINE('After delete, v_Numbers contains');
  Print(v_Numbers);

  v_Numbers.EXTEND(2, 1);
  DBMS_OUTPUT.PUT_LINE(' After extend, v_Numbers contains');
  Print(v_Numbers);

  DBMS_OUTPUT.PUT_LINE(' v_Numbers.COUNT = ' || v_Numbers.COUNT);
  DBMS_OUTPUT.PUT_LINE(' v_Numbers.LAST = ' || v_Numbers.LAST);
END;
7、TRIM
  TRIM被用来从嵌套表或可变数组的末端删除元素。
  它有两种形式:TRIM和TRIM(n)
  没有参数时,TRIM从集合的末端删除一个元素。否则,则删除n个元素。如果n大于COUNT,则抛出异常。因为删除了一些元素,在TRIM操作后COUNT将变小。TRIM也对集合内部的
大小进行操作,包括以DELETE删除的任何元素。
例如:
DECLARE
  v_Numbers NumTab := NumTab(-3, -2, -1, 0, 1, 2, 3);
  PROCEDURE Print(p_Table  IN  NumTab) IS
   v_Index  INTEGER;
  BEGIN
   v_Index := p_Table.FIRST;
   WHILE v_Index <= p_Table.LAST  LOOP
     DBMS_OUTPUT.PUT('Element ' || v_Index || ': ');
     DBMS_OUTPUT.PUT_LINE(p_Table(v_Index));
     v_Index := p_Table.NEXT(v_Index);
   END LOOP;
   DBMS_OUTPUT.PUT_LINE(' COUNT = ' || p_Table.COUNT);
   DBMS_OUTPUT.PUT_LINE(' LAST = ' || p_Table.LAST);
  END Print;
BEGIN
  DBMS_OUTPUT.PUT_LINE(' At initialization, v_Numbers contains');
  Print(v_Numbers);

  v_Numbers.DELETE(6);
  DBMS_OUTPUT.PUT_LINE(' After delete, v_Numbers contains');
  Print(v_Numbers);

  v_Numbers.TRIM(3);
  DBMS_OUTPUT.PUT_LINE(' After trim, v_Numbers contains');
  Print(v_Numbers);
END;
8、DELETE
  DELETE将从index-by表和嵌套表中删除一个或多个元素。DELETE对可变数组没有影响,因为它的大小固定(事实上,在可变数组上调用DELETE是不合法的)。
  DELETE有三种形式:
  DELETE
  DELETE(n)
  DELETE(m, n)
  没有参数时,DELETE将删除整个表。DELETE(n)将在索引n处删除一个元素,而DELETE(m, n)将删除索引m和n之间的所有元素。在DELETE操作之后,COUNT将变小,它反映了
嵌套表的新的大小。如果要删除的表元素不存在,DELETE不会引起错误,而是仅仅跳过那个元素。
例如:
DECLARE
  v_Numbers NumTab := NumTab(10, 20, 30, 40, 50, 60, 70, 80, 90, 100);
  PROCEDURE Print(p_Table IN NumTab) IS
   v_Index INTEGER;
  BEGIN
   v_Index := p_Table.FIRST;
   WHILE v_Index <= p_Table.LAST LOOP
    DBMS_OUTPUT.PUT(' Element ' || v_Index || ': ');
    DBMS_OUTPUT.PUT_LINE(p_Table(v_Index));
    v_Index := p_Table.NEXT(v_Index);
   END LOOP;
   DBMS_OUTPUT.PUT_LINE(' COUNT = ' || p_Table.COUNT);
   DBMS_OUTPUT.PUT_LINE(' LAST = ' || p_Table.LAST);
  END;
BEGIN
  DBMS_OUTPUT.PUT_LINE(' At initialization, v_Numbers contains' );
  Print(v_Numbers);
  
  DBMS_OUTPUT.PUT_LINE(' After delete(6), v_Numbers contains' );
  v_Numbers.DELETE(6);
  Print(v_Numbers);
 
  DBMS_OUTPUT.PUT_LINE(' After delete(7,9), v_Numbers contains‘);
  v_Numbers.DELETE(7,9);
  Print(v_Numbers);
END;

必须先使用type进行定义方可使用

1.index_by表

type type_name is table of element_type [NOT NULL] index by binary_integer

2.嵌套表

type type_name is table of element_type [NOT NULL]

3.varray

type type_name is [varray ¦varying array](max_size) of element_type[NOT NULL]

一,index_by表

TYPE TYPE1 IS TABLE OF VARCHAR2(10) INDEX BY BINARY_INTEGER;

1.使用的时候需要先赋值后读取,至少也要先初期化一下,否则会出现异常:ORA-01403: no data found。

2.这种数组不需要事先指定上限,下标可以不连续,可以是0或负数。

例:v1 TYPE1;

v1(-1) := '-1';
v1(0) := '0';
v1(1) := '1';
DBMS_OUTPUT.put_line(v1(-1)); --访问合法

DBMS_OUTPUT.put_line(v1(2)); --访问非法


二,嵌套表

TYPE TYPE2 IS TABLE OF VARCHAR2(10);

1.必须进行初期化,否则会出现异常:ORA-06531: Reference to uninitialized collection

2.初期化方法:

v1 TYPE2 := TYPE2(); --声明时初期化数组为空

v2 TYPE2 := TYPE2('1','2','3','4','5'); --声明时初期化数组为5个元素

v1 := TYPE2(); --初期化后数组为空

v2 := TYPE2('1','2','3','4','5'); --初期化后数组为5个元素

3.数组元素的访问:

下标从1开始,不能超过数组所有元素的总和,当下标超出允许范围时,出现异常:ORA-06532: Subscript outside of limit

因为不能访问空数组,所以空数组的场合,必须进行数组扩展。

例:v1.EXTEND;

V1(1):= ‘1’; --访问合法

v1(2):= ‘2’; --访问非法,之前必须再次执行v1.EXTEND;

例:v2的下标范围是1~5。

v2(5):= ‘Hello’; --访问合法

DBMS_OUTPUT.put_line(v2(6)); --访问非法

三,Varray

TYPE TYPE3 IS ARRAY(5) OF VARCHAR2(10);

由于类型定义时的元素个数限制,所以TYPE3的变量在使用时最大的元素个数不能超过5个。

与嵌套表基本相同(略)

四,集合内建函数

集合还有很多内建函数,这些函数称为方法,调用方法的语法如下:

collection.method

下表中列出oracle中集合的方法

方法 描述 使用限制

COUNT 返回集合中元素的个数

DELETE 删除集合中所有元素

DELETE(x) 删除元素下标为x的元素,如果x为null,则集合保持不变 对VARRAY非法

DELETE(x,y) 删除元素下标从X到Y的元素,如果X>Y集合保持不变 对VARRAY非法

EXIST(x) 如果集合元素x已经初始化,则返回TRUE, 否则返回FALSE

EXTEND 在集合末尾添加一个元素 对Index_by非法

EXTEND(x) 在集合末尾添加x个元素 对Index_by非法

EXTEND(x,n) 在集合末尾添加元素n的x个副本 对Index_by非法

FIRST 返回集合中的第一个元素的下标号,对于VARRAY集合始终返回1。

LAST 返回集合中最后一个元素的下标号, 对于VARRAY返回值始终等于COUNT。

LIMIT 返回VARRY集合的最大的元素个数,对于嵌套表和Index_by集合无用。

NEXT(x) 返回在元素x之后及紧挨着它的元素的值,如果该元素是最后一个元素,则返回null。

PRIOR(x) 返回集合中在元素x之前紧挨着它的元素的值,如果该元素是第一个元素,则返回null。

TRIM 从集合末端开始删除一个元素 对index_by不合法

TRIM(x) 从集合末端开始删除x个元素 对index_by不合法

index-by table类型,这个类型只能在过程定义,因此不能在返回函数中使用。
 
 
declare
  -- 定义
 
 type  t_a  is table  of  number(4) index by pls_integer;
 a   t_a;
 
   type  t_b  is table  of  number(10,2) index by varchar2(20);
 b   t_b;
begin
 
--赋值
 
a(1) := 123;
a(2) := 456;
--a(3) := 456;
a(4) := 888;
 
b('CHINA') :=  13000;
 
b('AMERCIAN') := 300000;
 --输出
 fori in 1..a.count loop
 --ifa(i).exists then    --错误的写法
 ifa.exists(i) then  --正确的写法
 dbms_output.put_line(a(i));
 endif;
 endloop;
 
 
 dbms_output.put_line('------------------------------------------------------');
 
 ifb.exists('CHINA') then  --正确的写法
 dbms_output.put_line(b('CHINA') );
 endif;
 
end;
 
oracle数据库提供了类似的一种集合,这个集合可以在过程中定义,也可以在过程外定义,其方法与pl/sql table类型的集合相似。
DECLARE
TYPE NumList IS TABLE OF NUMBER;
--这里没有index by
n NumList := NumList(1,3,5,7);
---需要初始化
counter INTEGER;
BEGIN
n.DELETE(2); --删除第二条记录
 
counter := n.FIRST; --实际上是1
WHILE counter IS NOT NULL
LOOP
DBMS_OUTPUT.PUT_LINE
('Counting up: Element #' || counter || ' =' || n(counter));
counter := n.NEXT(counter);
END LOOP;
-- Run the same loop in reverse order.
counter := n.LAST;
WHILE counter IS NOT NULL
LOOP
DBMS_OUTPUT.PUT_LINE
('Counting down: Element #' || counter || '= ' || n(counter));
counter := n.PRIOR(counter);
END LOOP;
END;
 
还有一种集合类型 varry,但是运用很少。
 
集合的定义与方法很简单,了解其他编程语言的数组,很容易理解这些概念。
然而oracle的集合却是oracle提高效率,灵活实现功能的一种重要方法。
Oracle提高了表函数能够将一个集合映射为一个表,这个表可以与其他数据集合一些使用(仅限于select,不能通过DML修改集合类型。)
 
集合占用了内存,了解集合占用内存情况很有必要
 
集合内存释放情况
 
win xp  oracle11g
DECLARE
TYPEdnames_tab IS TABLE OF char(2000);
 
dept_names dnames_tab := dnames_tab();
empty_set dnames_tab;
BEGIN
 
select rownum||'**' bulk collect into dept_names from dual connect by level<=100000;
 
ifdept_names is not null then
DBMS_OUTPUT.PUT_LINE(dept_names.count);
else
DBMS_OUTPUT.PUT_LINE('null');
end if;
 
dept_names := empty_set;
 
ifdept_names is not null then
DBMS_OUTPUT.PUT_LINE(dept_names.count);
else
DBMS_OUTPUT.PUT_LINE('null');
end if;
dbms_lock.sleep(10);
END;
 
运行上面的程序,观察系统内存比变化情况,发现只有程序运行时,使用内存在增加,dept_names :=empty_set;
后内存并没有立即释放,而是等到增个程序end的时候,才被释放。
说明:oracle的官方文档在这方面的介绍有一些问题:
oracle的官方文档说,一个集合trim后,会释放其占用的内存空间,但是我的测试结果并不支持这样的说法。
 
 
因此在程序中尽量避免大集合长期霸占内存,或者大集合多次拷贝
 
集合方法
集合的一些常用方法
窗体顶端
方法窗体底端
描述
使用限制
Count
返回集合中元素的个数
Delete
删除集合中所有元素
Delete(x)
删除元素下标为x的元素,如果x为null,则集合保持不变
对varray非法
Delete(x,y)
删除元素下标从x到y的元素,如果x>y则集合保持不变
对varray非法
Exists(x)
如果集合元素x已经初始化,则返回ture否则返回false
Extend
在集合末尾添加一个元素
对index_by非法
Extend(x)
在集合末尾添加x个元素
对index_by非法
Extend(x,n)
在集合末尾添加元素n的x个副本
对index_by非法
First
返回集合第一个元素的下标号,对于varray集合始终返回1
Last
返回集合中最后一个元素的下标号,对于varray集合返回值始终等于count
Limit
返回varry集合的最大元素个数,对于嵌套表和index_by始终为null
Index_by和嵌套表无效
Next()
返回元素x之后紧挨着他的元素的值,如果该元素是最后一个元素,则返回null
Prior()
返回集合中元素在x之前紧挨着他的元素的值,如果该元素是第一个元素,则返回null
Trim
从集合末端开始删除一个元素
Trim(x)
从集合末端开始删除x个元素
  窗体底端
 
上面提到的很多方法,在实际开发中较少使用到,无须记住它们。
 
似是而非的问题(trim|delete|count|first..last)
 
DECLARE
TYPE nested_typ IS TABLE OF NUMBER;
  nt1nested_typ := nested_typ(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12);
  BEGIN
 
   dbms_output.put_line('nt1.count=' || nt1.count);
 
   for i in 1 .. nt1.count loop
     if nt1(i) is not null then
       dbms_output.put_line(nt1(i));
     end if;
   end loop;
    dbms_output.put_line('************************************');
   nt1.delete(4);
   nt1.delete(6);
   nt1.delete(8);
 
   dbms_output.put_line('nt1.count=' || nt1.count);
   
   for i in 1 .. nt1.count loop
     if nt1.exists(i) then
       dbms_output.put_line(nt1(i));
     else
        dbms_output.put_line('位置在 但是没有'); 
     end if;
   end loop;
    dbms_output.put_line('************************************');  
   for i in nt1.first .. nt1.last loop
     if nt1.exists(i) then
       dbms_output.put_line(nt1(i));
     else
        dbms_output.put_line('位置在 但是没有');         
     end if;
   end loop;
     dbms_output.put_line('************************************');        
 end;
 
输出结果
nt1.count=12
1
2
3
4
5
6
7
8
9
10
11
12
************************************
nt1.count=9
1
2
3
位置在 但是没有
5
位置在 但是没有
7
位置在 但是没有
9
************************************
1
2
3
位置在 但是没有
5
位置在 但是没有
7
位置在 但是没有
9
10
11
12
************************************
看到如果集合还是变得稀疏,使用count 就开始产生疑义,最好采用first last。
 
 
 
单字段集合
declare
  --声明一个集合对象typ_number 用于存放number(10,2)的数组
 type typ_number is table of number(10,2);
  --声明一个对象typ_number的实例并初始化
 sal  typ_number := typ_number();
 
 i_rowcounts  integer;
begin
  --扩展一个组员
 sal.extend(1);
  --赋予一个组员值
 sal(sal.last) :=100.11;
 
  --利用循环赋值
  fori in 1..100 loop
   sal.extend(1);
   sal(sal.last) := sqrt(i);
  endloop;
  --输出
 dbms_output.put_line(sal.count);
 
  sal.extend(100);
  for i in 100..200 loop
   sal(i+1) := sqrt(i);
  endloop;
  dbms_output.put_line(sal(200));
  
   --将select 的结果装入数组
  
  select  count(*) intoi_rowcounts  from scott.emp;
  dbms_output.put_line(i_rowcounts);
    
   ifi_rowcounts>0 then
   select sal s  bulk collect intosal from scott.emp;
  end if;
  
  for  k in 1.. sal.count loop
   dbms_output.put_line(sal(k));
  end loop;
   --请问现在集合sal有多少个元素?
   --注意 如果采用bulk collect into 则这个集合从新初始化了
 
   --动态语句sql的集合赋值
   sal.delete;
  dbms_output.put_line(sal.count);
  
  sql_stmt := 'select sal from scott.emp';
  execute immediate sql_stmt  bulk collectinto sal;
   dbms_output.put_line(sal.count); 
end;
 
尽管可以用游标来赋值,但是我们还是力主用collect bulk into来赋值。
 
多个字段的集合的赋值方法
定义一个多字段的对象
create type myScalarType as object( x int, y date, z varchar2(30));
定义一个基于该对象的集合
create type myArrayType as table of myScalarType;
 
 
declare
   l_data myArrayType :=myArrayType();
begin
   l_data.extend;
   l_data(1).x := 42;
   l_data(1).y := sysdate;
   l_data(1).z := 'hello world';
   l_data.extend;
   l_data(2) := myScalarType( 1, sysdate, 'x' );
end;
 
如果在扩展后,l_data(l_data.count).x:= 42;操作成功
但是l_data(l_data.count).y:= sysdate-100; 却出现了错误
或者直接l_data(l_data.count).y:= sysdate-100;也出现了错误
 
正确的做法是
l_data(1):=myScalarType(1,sysdate,'ggg');
或者
l_data(1):=myScalarType(null,null,null);
再赋值
declare
   l_data myArrayType := myArrayType();
begin
   l_data.extend;
   l_data(1):=myScalarType(1,sysdate,'ggg');
   dbms_output.put_line(l_data(1).y);
end;
 
但是对于本地集合plsql index by (sparse) table
DECLARE
 TYPE r_prods is RECORD (Prod_no number(1),
                        DAY_IN_WEEK  number(1),
                        BRANCH_NO     NUMBER(1),
                         QTY           NUMBER(4));
 
 TYPE t_prods IS TABLE OF r_prods INDEX BY BINARY_INTEGER;
 v_Prods t_prods;
BEGIN
   v_Prods(111).prod_no :=1;
   v_Prods(111).day_in_week :=1;
   v_Prods(111).branch_no :=1;
   v_Prods(111).qty:=300;
   v_Prods(121).prod_no :=1;
   v_Prods(121).day_in_week :=2;
   v_Prods(121).branch_no :=1;
   v_Prods(121).qty:=400;
  -- dbms_output.put_line(l_data(1).y);
end;
却成功
 
 
Limit
 
Limit是9i以后版本引进的
 
注意点:
1、limit 的参数不是越大越好
2、退出的写法与位置
 
注意游标退出的判断以及位置
 
DECLARE
TYPE numtab IS TABLEOF NUMBER INDEX BY PLS_INTEGER;
CURSOR c1 IS SELECT hr.employee_idFROM employees WHERE department_id = 80;
empids numtab;
rows PLS_INTEGER :=10;
BEGIN
OPEN c1;
LOOP -- the following statement fetches 10 rows or less ineach iteration
FETCH c1 BULKCOLLECT INTO empids LIMIT rows;
EXIT WHENempids.COUNT = 0;
-- EXIT WHENc1%NOTFOUND; --不正确, 这样写可能或略部分数据
DBMS_OUTPUT.PUT_LINE('-------Results from Each Bulk Fetch --------');
FOR i IN 1..empids.COUNT LOOP
DBMS_OUTPUT.PUT_LINE('Employee Id: ' || empids(i));
END LOOP;
END LOOP;
CLOSE c1;
END;
 
 
 
 
 
 
 
 
 
 
 
 
集合的运算以及集合与表运算
 
drop type type_emp
create or replace type emp_t as object
(
empno  number(4,0),
ename  varchar2(20),
sal   number(16,2)
)
 
 
测试
selectemp_t(empno,ename,sal) from emp;
 
drop type emp_t
create type table_emp is table of emp_t
 
declare
  i integer;
  e table_emp;
  e1 table_emp;
  v_sal number(16,0);
 
  type c_sal is table of number(10,2);
  v_c c_sal;
begin
  select emp_t(empno,ename,sal) bulk collect into e from emp;
  dbms_output.put_line(e.count);
 
  for i in 1..e.count loop
  dbms_output.put_line(e(i).empno||'      '||e(i).sal);
  end loop;
 
  /*
  for i in1..e.count loop
  e(i).sal:=(selectsum(sal) from emp where empno=e(i).empno);
  end loop;
 */
 
  for i in 1..e.count loop
  select sal into v_sal from emp where empno=e(i).empno;
  e(i).sal:=e(i).sal+v_sal;
  end loop;
 
  for i in 1..e.count loop
  dbms_output.put_line(e(i).empno||'      '||e(i).sal);
  end loop;
 
  select sal bulk collect into v_c from emp;
 
  for i in 1..e.count loop
  e(i).sal:=v_c(i);
  end loop;
 
  for i in 1..e.count loop
  dbms_output.put_line(e(i).empno||'      '||e(i).sal);
  end loop;
 
  select
  emp_t(empno,ename,sal) bulk collect into e1
  from table(e);
 
  dbms_output.put_line('print e1'); 
 
  for i in 1..e1.count loop
  dbms_output.put_line(e1(i).empno||'      '||e1(i).sal);
  end loop; 
 
  e1.delete;
 
  select
  emp_t(a.empno,a.ename,a.sal+b.sal)bulk collect into e1
  from table(e)a, emp b
  where a.empno=b.empno;
 
  dbms_output.put_line('print e1 withtable'); 
 
  for i in 1..e1.count loop
  dbms_output.put_line(e1(i).empno||'      '||e1(i).sal);
  end loop;
 
--对于表,我们可以
/*
update   emp a
set a.sal= 1+(select sal from emp wherea.empno=emp.empno);
*/
 
 
--update   table(e)a
--set a.sal= 1+(select sal from emp wherea.empno=emp.empno);
--但是对于集合 则 
--ora-00903错误 表名无效
 
for i in 1..e.count loop
for j in 1..e1.count loop
if e1(j).empno=e(i).empnothen
e(i).sal:=nvl(e(i).sal,0)+nvl(e1(j).sal,0);
end if;
end loop;
end loop;
 
dbms_output.put_line('col operate with other col');
  for i in 1..e1.count loop
  dbms_output.put_line(e1(i).empno||'      '||e1(i).sal);
  end loop;
end;
 
 
集合排序与拷贝
declare
  i integer;
  e table_emp;
  e_order table_emp;
  e1 table_emp;
  v_sal number(16,0);
  type c_sal is table of number(10,2);
  v_c c_sal;
 
begin
  select emp_t(empno,ename,sal) bulk collect into e from emp;
  dbms_output.put_line(e.count);
 
  for i in 1..e.count loop
  dbms_output.put_line(e(i).empno||'      '||e(i).sal);
  end loop;
 
  select count(*) into i from table(e);
  dbms_output.put_line('i from e '||i);
 
  select emp_t(empno,ename,sal)
  bulk collect into e_order
  from table(e) order by sal;
  dbms_output.put_line('print e_order'); 
  for i in 1..e.count loop
 dbms_output.put_line(e_order(i).empno||'     '||e_order(i).sal);
  end loop;
end;
 
或者常用方法进行排序,这样可以节省内存空间
declare
 -- Local variables here
 type table_sal is table of scott.emp.sal%type;
 c_sal table_sal;
 v_sal  scott.emp.sal%type :=0;
 point integer;
begin
select sal bulk collect intoc_sal from emp;
 
for i in 1..c_sal.count loop
 dbms_output.put_line(c_sal(i));
end loop;
 
 dbms_output.put_line('----------------------------------');
for i in 2..c_sal.count loop
for i in 2..c_sal.count loop
if c_sal(i) > c_sal(i-1) then
 v_sal := c_sal(i-1);
 c_sal(i-1) :=c_sal(i);
 c_sal(i) := v_sal;
end if ;
end loop;
end loop;
 
for i in 1..c_sal.count loop
 dbms_output.put_line(c_sal(i));
 end loop;
end;
 
输出
-----------------------------------------------------------
800
1600
1250
2975
1250
2850
2450
3000
5000
1500
1100
950
3000
1300
----------------------------------
5000
3000
3000
2975
2850
2450
1600
1500
1300
1250
1250
1100
950
800
 
如果一个集合中的元素被删除,调用这个元素则会出错
declare
 i integer;
 e table_emp;
 e1 table_emp;
 e2 table_emp; 
 v_sal number(16,0);
 
 type c_sal is table of number(10,2);
 v_c c_sal;
begin
 select emp_t(empno,ename,sal) bulk collect into e from emp;
 dbms_output.put_line(e.count);
 
 for i in 1..e.count loop
 dbms_output.put_line(nvl(e(i).empno,0)||'      '||nvl(e(i).sal,0));
 end loop;
e.delete(1);
--e.trim(13);
 
--集合第一条数据被删除
--结果集合的条数从3变成2
--但是 如果想输出集合 则出现错误
 
 -- for i in 1..e.count loop
 --dbms_output.put_line(nvl(e1(i).empno,0)||'   '||nvl(e1(i).sal,0));
 --end loop;
--错误
 
--解决方法 1  作以下判断
 for i in 1..e.count loop
 if e1.exists(i) then
 dbms_output.put_line(nvl(e1(i).empno,0)||'     '||nvl(e1(i).sal,0));
 end if;
 end loop;
 
 
--解决方法2  将删除后的集合 放到一个新的集合中
 
select emp_t(empno,ename,sal)bulk collect into e1 from table(e);
 dbms_output.put_line(e1.count);
 
 for i in 1..e.count loop
 dbms_output.put_line(nvl(e1(i).empno,0)||'      '||nvl(e1(i).sal,0));
 end loop;
end;
 
DECLARE
 TYPE NumList IS TABLE OF NUMBER;
 n NumList := NumList(10,20,30,40,50,60,70,80,90,100);
 
 BEGIN
 dbms_output.put_line(n.count);
 
 n.DELETE(2); -- deletes element 2
 dbms_output.put_line(n.count);
 
 for i in 1..n.count loop
 dbms_output.put_line(n(i));
 end loop;
 
 end;
 
第 1 行出现错误:
ORA-01403: 未找到任何数据
ORA-06512: 在 line 12
 
修改
for i in 1..n.count loop
 if n.exists(i) then
 dbms_output.put_line(n(i));
 end if;
 end loop;
 
 
集合运算
IN | not IN
 
 
DECLARE
 TYPE nested_typ IS TABLE OF NUMBER;
 nt1  nested_typ := nested_typ(1,2, 3);
 nt2  nested_typ := nested_typ(1,2, 3, 4);
 flag boolean := true;
 p    integer := 0;
BEGIN
 
 if nt1 IN (nt2) then
   dbms_output.put_line('true');
 else
   dbms_output.put_line('false');
 end if;
 
 --输出 为flase  这不是我们所需要的结果
 
 for i in 1 .. nt1.count loop
   for j in 1 .. nt2.count loop
     if nt1(i) = nt2(j) then
        p := p + 1;
     end if;
   end loop;
   if p > i then
     p := i;
   end if;
   if p < i then
     flag := false;
   end if;
   exit when p < i;
 end loop;
 
 if flag = true then
   dbms_output.put_line('true');
 else
   dbms_output.put_line('false');
 end if;
 --这是我们所需要的结果
END;
可以将上述过程编写成函数。
 
Distinct
DECLARE
 TYPE nested_typ IS TABLE OF NUMBER;
 nt1  nested_typ :=nested_typ(100,1, 2, 3, 6,6,6, 19,9,7,11,3, 2, 12,1,14, 3,11,1,11,1,13,109);
 collect_count integer;
BEGIN
 
 dbms_output.put_line('nt1.count=' || nt1.count);
 collect_count := nt1.count;
 for i in nt1.first .. nt1.last loop
   for j in nt1.first .. nt1.last loop
   --注意 不能写成  1.. nt1.count
     if nt1.exists(i) and nt1.exists(j) then
        if i <> j and nt1(i) = nt1(j)then
          nt1.delete(j);
        end if;
     end if;
   end loop;
 end loop;
 
 dbms_output.put_line('nt1.count=' || nt1.count);
 for i in nt1.first .. nt1.last loop
   if nt1.exists(i) then
     dbms_output.put_line(nt1(i));
   end if;
 end loop;
END;
-------------------------------------------
100
1
2
3
6
19
9
7
11
12
14
13
109
 
 
union all
思想 找出较少元素的结合,通过loop将元素增加到较多元素的集合中,程序省略
 
union
union all + distinct
或者自己重新编写程序
 
intersect
自己重新编写程序
 
intersect
自己重新编写程序
 
 
利用oracle的集合运算符(unionall | union | minus | intersect) 对集合进行集合运算
利用表函数将集合转化为可以select的表,在做集合运算
好处是简化程序 弊端为分配一个新的内存
 
create TYPE nested_typ IS TABLE OF NUMBER;
 
DECLARE
nt1 nested_typ := nested_typ(1,2,3,5);
nt2 nested_typ := nested_typ(3,2,1,4);
nt3 nested_typ := nested_typ(2,3,1,3);
nt4 nested_typ := nested_typ(1,2,4);
nt5 nested_typ := nested_typ();
BEGIN
 
for i in 1..nt1.count loop
  for j in 1..nt2.count loop
  if nt1(i)=nt2(j) then
   dbms_output.put_line(nt1(i));
   end if;
  end loop;
end loop;
 
 
select a
bulk collect into nt5
from
(
select column_value a from table(nt1)
union all
select column_value a from table(nt2)
);
 
 
for i in 1..nt5.count loop
   dbms_output.put_line(nt5(i));
end loop;
 
END;
 
 
负集
create TYPE NumList IS TABLE OF NUMBER;
 
DECLARE
n2 NumList:= NumList(10,20,35,90);
n1NumList:= NumList(10,20,30,40,50,60,70,80,90,100);
n3 NumList := NumList();
BEGIN
 
select a
bulk collect into n3
from
(
select column_value a fromtable(n1)
minus
select column_value a fromtable(n2)
);
 
for i in 1..n3.count loop
dbms_output.put_line(n3(i));
end loop;
end;
 
 
集合的运算
注意集合类型一定在外定义
不能在程序内部定义
create or replace TYPE NumList IS TABLE OF integer;
 
DECLARE
不能在程序内部定义
--TYPE NumList IS TABLE OF NUMBER;
n NumList := NumList(10,20,35);
n1 NumList:= NumList(10,20,30,40,50,60,70,80,90,100);
n3 NumList;
i integer;
BEGIN
 
 
selectNumList(a) bulk_collect into n3
from
(
selectcolumn_value a from table(n)
minus
selectcolumn_value a from table(n1)
);
 
for i in 1..n3.count loop
dbms_output.put_line(n3(i));
end loop;
end;
 
集合与表的联合运算
 
declare
--不能在程序内部定义
--TYPE NumList IS TABLE OF NUMBER;
n NumList := NumList(10,20,35);
n1 NumList:= NumList(10,20,30,40,50,60,70,80,90,100);
n3 NumList;
i integer;
BEGIN
 
select a.empno bulk collect into n3  from
emp a,table(n) b
wherea.deptno=b.column_value;
 
for i in 1..n3.count loop
dbms_output.put_line(n3(i));
end loop;
end;
 
以上我们看到集合的方法以及自定义的集合运算方法,我们也了解到了从数据库的结果集批量取数到集合中的方法。
 
下面介绍更重要的集合数据的物化问题,就是将集合数据dml表中。
FORALL技术
 
先看一个基本的例子
 
create table TARGET_ALL_OBJECT
(
  OWNER          VARCHAR2(30),
  OBJECT_NAME    VARCHAR2(30),
  SUBOBJECT_NAME VARCHAR2(30),
  OBJECT_ID      NUMBER,
  DATA_OBJECT_ID NUMBER,
  OBJECT_TYPE    VARCHAR2(19),
  CREATED        DATE,
  LAST_DDL_TIME  DATE,
  TIMESTAMP      VARCHAR2(19),
  STATUS         VARCHAR2(7),
  TEMPORARY      VARCHAR2(1),
  GENERATED      VARCHAR2(1),
  SECONDARY      VARCHAR2(1),
  NAMESPACE      NUMBER,
  EDITION_NAME   VARCHAR2(30)
)
 
declare
          cursor c1 is select * from cur_all_object slow_by_slow;
          cursor c2 is select * from cur_all_object  bulk_collect;
 
          type array is table of c1%rowtype index by binary_integer;
          l_array array;
           l_row  c1%rowtype;
          start_time int;
  begin
         start_time := dbms_utility.get_time;
          open c1;
          loop
                   fetch c1 into l_row;
                   exit when c1%notfound;
                   insert intotarget_all_object(object_id) values(l_row.object_id);
          end loop;
          close c1;
        dbms_output.put_line(( dbms_utility.get_time-start_time)/100);
        commit;
        
        execute immediate 'truncate table target_all_object ';
 
         start_time := dbms_utility.get_time;
          open c2;
          loop
                   fetch c2 bulk collect intol_array limit 10000;
                   -- process rows here --
                   exit when c2%notfound;
                  
                   for m in 1..l_array.countloop
                   insert intotarget_all_object(object_id)  values(l_array(m).object_id);
                   end loop;
                  
          end loop;
          close c2;
          dbms_output.put_line(( dbms_utility.get_time-start_time)/100);
          commit;
          execute immediate 'truncate table target_all_object '; 
                  
        start_time := dbms_utility.get_time;
          open c2;
          loop
                  fetch c2 bulk collectinto l_array limit 10000;
                   -- process rows here --
                   exit when c2%notfound;
                  
                   forall m in 1..l_array.count
                   insert into target_all_object(object_id)  values (l_array(m).object_id);
                  
          end loop;
          close c2;
          dbms_output.put_line(( dbms_utility.get_time-start_time)/100);
          commit;
          execute immediate 'truncate table target_all_object ';         
  end;
--------
4.22
2.93
0.34
 
limit=100
--------
4.23
2.86
0.36
 
插入全表的数据
 
 
declare
           cursor c1 is select * fromcur_all_object slow_by_slow;
           cursor c2 is select * fromcur_all_object  bulk_collect;
 
           type array is table of tiwen.target_all_object%rowtype  index by binary_integer;
           l_array array;
           l_row   tiwen.target_all_object%rowtype;
           start_time int;
   begin
          start_time := dbms_utility.get_time;
           open c1;
           loop
                   fetch c1 into l_row;
                   exit when c1%notfound;
                   insert into target_all_object  values  l_row;
           end loop;
           close c1;
         dbms_output.put_line((dbms_utility.get_time-start_time)/100);
         commit;
        
         execute immediate 'truncate tabletarget_all_object ';
 
          start_time := dbms_utility.get_time;
           open c2;
           loop
                   fetch c2 bulk collect intol_array limit 200;
                   -- process rows here --
                   exit when c2%notfound;
                  
                   for m in 1..l_array.countloop
                   insert into target_all_object  values l_array(m);
                   end loop;
                  
           end loop;
           close c2;
           dbms_output.put_line((dbms_utility.get_time-start_time)/100);
           commit;
           execute immediate 'truncate tabletarget_all_object '; 
                  
         start_time := dbms_utility.get_time;
           open c2;
           loop
                   fetch c2 bulk collect into l_array limit10000;
                   -- process rows here --
                   exit when c2%notfound;
                  
                   forall m in 1..l_array.count
                   insert into target_all_object valuesl_array(m);
                  
           end loop;
           close c2;
           dbms_output.put_line((dbms_utility.get_time-start_time)/100);
           commit;
           execute immediate 'truncate tabletarget_all_object ';         
   end;
 
 
或者
declare
          cursor c1 is select * from cur_all_object slow_by_slow;
          cursor c2 is select * from cur_all_object  bulk_collect;
 
           type array is table of c1%rowtypeindex by binary_integer;
           l_array array;
           l_row   c1%rowtype;
          start_time int;
  begin
         start_time := dbms_utility.get_time;
          open c1;
          loop
                   fetch c1 into l_row;
                   exit when c1%notfound;
                   insert into target_all_object  values l_row;
          end loop;
          close c1;
        dbms_output.put_line(( dbms_utility.get_time-start_time)/100);
        commit;
        
        execute immediate 'truncate table target_all_object ';
 
         start_time := dbms_utility.get_time;
          open c2;
          loop
                   fetch c2 bulk collect intol_array limit 10000;
                   -- process rows here --
                   exit when c2%notfound;
                  
                   for m in 1..l_array.countloop
                   insert into target_all_object  values l_array(m);
                   end loop;
                  
          end loop;
          close c2;
          dbms_output.put_line(( dbms_utility.get_time-start_time)/100);
          commit;
          execute immediate 'truncate table target_all_object '; 
                  
        start_time := dbms_utility.get_time;
          open c2;
          loop
                   fetch c2 bulk collect intol_array limit 10000;
                   -- process rows here --
                   exit when c2%notfound;
                  
                   forall m in 1..l_array.count
                   insert into target_all_object  values l_array(m);
                  
          end loop;
          close c2;
          dbms_output.put_line(( dbms_utility.get_time-start_time)/100);
          commit;
          execute immediate 'truncate table target_all_object ';         
  end;
 
 
看到 FORALL 极大地提高了效率,与取数一样,forall避免了工作引擎之间的切换。
 
注意写法上的差别
 
                   for m in 1..l_array.countloop
                   insert intotarget_all_object  values l_array(m);
                   end loop;
 
                   forall m in 1..l_array.count
                   insert into target_all_object  values l_array(m);
 
FORALL后面直接跟上dml语句,中间不能有其他如判断过程。如
                   forall m in 1..l_array.count
                    if mod(object_id,2)=1 then
                       insert into target_all_object  values l_array(m);
                   end if;
 
 
结束时候 没有END LOOP这句话
 
                   forall m in 1..l_array.count
                   insert intotarget_all_object  values l_array(m);
不支持稀疏集合

declare
          cursor c1 is select * from cur_all_object slow_by_slow;
          cursor c2 is select * from cur_all_object  bulk_collect;
 
          type array is table of c1%rowtype index by binary_integer;
          l_array array;
          l_row   c1%rowtype;
          start_time int;
  begin                  
        start_time := dbms_utility.get_time;
          open c2;
          loop
                   fetch c2 bulk collect intol_array limit 10000;
                   -- process rows here --
                   exit when c2%notfound;
                  
                   l_array.delete(10);
                   forall m in 1..l_array.count
                   insert intotarget_all_object  values l_array(m);
                  
          end loop;
          close c2;
          dbms_output.put_line(( dbms_utility.get_time-start_time)/100);
          commit;
          execute immediate 'truncate table target_all_object ';         
  end;
 
ORA-22160: 下标 [10] 中的元素不存在
ORA-06512: 在 line 18
 
为解决这个问题,在oracle10个中提供了新的语法
 
declare
          cursor c1 is select * from cur_all_object slow_by_slow;
          cursor c2 is select * from cur_all_object  bulk_collect;
 
          type array is table of c1%rowtype index by binary_integer;
          l_array array;
          l_row   c1%rowtype;
          start_time int;
  begin                  
        start_time := dbms_utility.get_time;
          open c2;
          loop
                   fetch c2 bulk collect intol_array limit 10000;
                   -- process rows here --
                   exit when c2%notfound;
                  
                   l_array.delete(10);
                   forall m in INDICES OFl_array
                   insert intotarget_all_object  values l_array(m);
                 
          end loop;
          close c2;
          dbms_output.put_line(( dbms_utility.get_time-start_time)/100);
          commit;
          execute immediate 'truncate table target_all_object ';         
  end;
 
如果只想插入新数据的一部分 如第2,7,10个,oracle 10g中又提供了 in value的语法
 
declare
          cursor c1 is select * from cur_all_object slow_by_slow;
          cursor c2 is select * from cur_all_object  bulk_collect;
 
          type array is table of c1%rowtype index by binary_integer;
          l_array array;
          l_row   c1%rowtype;
          start_time int;
          
          TYPE aat_id IS TABLE OF PLS_INTEGERINDEX BY PLS_INTEGER;
          legal_ids aat_id;
  begin                  
        start_time := dbms_utility.get_time;
          open c2;
          loop
                   fetch c2 bulk collect intol_array limit 10000;
                   -- process rows here --
                   exit when c2%notfound;
                  
                   legal_ids(1) := 2;
                   legal_ids(2) := 7;
                   legal_ids(3) := 10;
                  
                   forall m IN VALUES OFlegal_ids
                   insert intotarget_all_object  values l_array(m);
                 
                  
          end loop;
          close c2;
          dbms_output.put_line(( dbms_utility.get_time-start_time)/100);
          commit;
          execute immediate 'truncate table target_all_object ';         
  end;
 
 
FORALL是oracle的一个很重要的技术选项,上面只是介绍了 FORALL insert操作,FORALL支持oracle的所有DML操作。
 
FORALL update
在我们讲到用一个数据集更新另一个数据集进行update操作的时候,update隐含着一个规则,即用于更新的数据源在关联条件上一定是唯一的。但是很多情况下,这个隐含的规则并不满足。
 

create table source_update
(
 seq    int,
 id     int,
 name  varchar2(10)
)
 
SEQ
ID
NAME
1
10
aaaa
2
20
bbbb
3
10
cccc
4
30
pppp
 
 
create table target_update
(
id     int,
 name  varchar2(10)
)
ID
NAME
10
mmmmmmmm
20
dddd
30
sss
40
aaavvv
 
merge into target_update a
using (select * from source_update)  b
on (a.id=b.id)
when matched then
update set name=b.name
 
ORA-30926: 无法在源表中获得一组稳定的行
 
可以取得最新的更新数据源
merge into target_update a
using (
select * from source_update
where seq in
(
select  max(seq) ms  from source_update group by id
)
)  b
on (a.id=b.id)
when matched then
update set name=b.name
 
成功
 
当然对于
select * from source_update
where seq in
(
select  max(seq) ms  from source_update group by id
)
我们可以有更加有效的写法
select id,name
from
(
select seq, max(seq) over (partition by id ) s, id, name from source_update
)
where seq=s
 
但是不是所有特征的数据情况下,这些写法具有高效率。 说明
 
但是 由于不能确认id具有唯一性,又不能直接用update(merge)的方法, 游标集合提供了很好的方法
 
declare
          cursor c2 is select id,name from source_update orderby seq;
          type array is table of c2%rowtype index by binary_integer;
          l_array array;
          start_time int;
  begin                  
        start_time := dbms_utility.get_time;
          open c2;
          loop
                   fetch c2 bulk collect intol_array limit 10000;
                   exit when c2%notfound;
                                      
                   forall m IN  l_array.first..l_array.last
                   update target_update
set name=l_array(m).name
                   where id= l_array(m).id;
          end loop;
          close c2;
          dbms_output.put_line(( dbms_utility.get_time-start_time)/100);
          commit;  
  end;
讨论:你能根据已有的知识,做相关表的物理设计,使上述过程效率得到更大提高吗?
 
FORALL的部分更新
CREATE TABLEemp_temp (deptno NUMBER(2), job VARCHAR2(18));
 
 --INSERT INTO emp_temp VALUES (10, 'Clerk');
  --Lengthening this job title causes an exception
 --INSERT INTO emp_temp VALUES (20, 'Bookkeeper');
 --INSERT INTO emp_temp VALUES (30, 'Analyst');
 --COMMIT;
 
DECLARE
 TYPE NumList IS TABLE OF NUMBER;
 depts NumList := NumList(10, 20, 30);
BEGIN
 FORALL j IN depts.FIRST .. depts.LAST -- Run 3 UPDATE statements.
   UPDATE emp_temp SET job = job || ' (Senior)' WHERE deptno = depts(j);
  --raises a "value too large" exception
EXCEPTION
  WHEN OTHERS THEN
   DBMS_OUTPUT.PUT_LINE('Problem in the FORALL statement.');
    COMMIT; -- Commit results of successfulupdates.
END;













联合数组嵌套表可变数组属性和方法Oracle 

PL/SQL语言的集合类似于数组,是管理多行数据必须的结构体。集合就是列表,可能有序,也可能无序。PL/SQL的集合类型有:联合数组、嵌套表和可变数组三种。
 
1、联合数组
联合数组类似于C语言中的数组。
(1)语法格式如下:
 
Sql代码  
type typeName  
is table of arrayType index by binary_integer;  
 
 
其中,typeName表示新类型的类型名,arrowType表示要定义的联合数组的类型。
假设有一个表t_module(name varchar(20)),那么我们可以针对其name字段建立如下联合数组:
 
Sql代码  
type moduleName is table of t_module.name%type index by binary_integer;  
 
 
(2)元素赋值
示例代码如下:
 
Sql代码  
declare  
    type NameArray  
    is table of varchar(20) index by binary_integer; /*定义联合数组NameArrow*/  
    na NameArray; /*声明联合数组NameArrow的一个变量*/  
begin  
    na(1) := 'Hello'; /*给联合数组赋值*/  
    na(2) := 'World'; /*给联合数组赋值*/  
    na(5) := 'Oracle'; /*给联合数组赋值*/  
    dbms_output.put_line(na(1) || na(2));  
end;  
 
 
注意:
a.联合数组中的元素不是按特定顺序排列的,元素的下标也是可以无序的,所以nameArrow(-100) := 'Hello'也是可以的。
b.index的数据类型是binary_integer类型,所以index的范围为-214483647~+214483647。
c.当调用不存在的元素时将会出错。

2、嵌套表
嵌套表的声明和联合数组的声明十分类似。语法格式如下:
 
Sql代码  
type typeName is table of arrayType [not null]  
 
嵌套表的声明与联合数组的唯一不同是没有index by binary_integer子句。
(1)嵌套表的初始化
嵌套表的初始化和联合数组的初始化是不同的。在声明了联合数组之后,再声明一个联合数组对应的变量,如果此时没有给该变量赋值,那么该变量对应的联合数组就是一个空的,在以后的语句中还可以给该联合数组添加元素;而如果在声明了嵌套表变量时没有初始化,则该嵌套表将自动初始化为null,并且是只读的。如果在后续代码中继续向该嵌套表中添加元素,就会报错,但是可以修改已经存在的元素的值。
以下是一个嵌套表的初始化示例:
 
Sql代码  
declare  
    type NameArray is table of varchar(20);  
    na NameArray := NameArray('ZhangSan', 'LiSi', 'WangWu'); /*在声明的时候即初始化该嵌套表*/  
    begin  
        for nameIndex in 1..3 loop  
            dbms_output.put_line(na(nameIndex));  
        end loop;  
    end;  
 
(2)元素序列
嵌套表与联合数组十分相似,只是嵌套表在结构上是有序的,而联合数组是无序的。嵌套表元素的index是从1开始递增的。

3、可变数组
先来看可变数组的语法格式:
 
Sql代码  
type typeName is varray | varying array(maximunSize) of arrayType [not null]  
 
maximunSize是指可变数组的最大值,arrayType表示数组元素的类型。
可变数组的可变是指当定义了数组的最大容量后,数组元素的个数可在这个范围内进行变动。
与嵌套表一样,可变数组也需要初始化,在初始化后也只能修改已经存在的元素值,初始化时赋值的数量必须不超过最大容量。
以下是一段示例代码:
 
Sql代码  
declare  
    type NameArray is varray(10) of varchar(20);  
    na NameArray := NameArray('ZhangSan', 'LiSi', 'WangWu');  
begin  
    for nameIndex in 1..3 loop  
        dbms_output.put_line(na(nameIndex));  
    end loop;  
end;  
 

4、集合的属性和方法
(1)count属性
count属性用于返回集合中数组元素的个数。
示例代码:
na.count; /*在上面例子中调用该语句将返回3*/
(2)delete方法
delete方法用于删除集合中的一个或多个元素。由于delete方法删除的是固定位置的元素,所以对应可变数组来说是没有
delete方法的。delete方法有3中形式:
delete:不带参数的delete方法将删除整个集合。
delete(index):将删除集合中第index个位置的元素。
delete(start, end):将删除集合中从start到end的元素。
注意:
a.执行delete方法后,集合的count值将会立即发生改变。
b.执行delete操作后,其他元素的index不会发生改变。
c.当要删除的元素不存在时,delete不会报错,而是跳过该元素,继续执行下一操作。
(3)exists方法
exists方法用于判断集合中指定位置的元素是否存在。
语法格式如下:
exists(index);
如果index位置的元素存在即返回true,否则就是false,如果index大于集合的最大范围,则返回false。
(4)extend方法
extend方法用于将元素添加到集合的末端,由于联合数组的随意性,所以该方法不能对联合数组使用,有以下几种形式:
extend:不带参数的extend方法将添加一个null元素到集合的末端
extend(n):将添加n个null元素到集合的末端。
extend(n, x):将添加n个x位置的元素到集合的末端。
(5)first属性和last属性
first属性用于返回集合中的第一个元素所在的位置,last属性用于返回集合中的最后一个元素所在的位置。
(6)limit属性
limit属性用于返回集合的最大容量,该属性只有对可变数组才有返回值,对其他的则返回null。
(7)next方法和prior方法
next方法和prior方法都有一个表示index的参数,语法格式为:
next(index);
prior(index);
next(index)返回集合中index后一个元素的位置,而prior(index)方法则返回集合中index位置前一个元素的位置,如果对应返
回的位置不存在则返回null。
(8)trim方法
trim方法用于从集合的末端删除元素,由于联合数组的随意性,所以不能对联合数组使用trim方法,有以下两种形式:
trim:不带参数的trim从集合末端删除一个元素。
trim(n):从集合末端删除n个元素,其中n不能超出集合的count数。

猜你喜欢

转载自zhyp29.iteye.com/blog/2302202
今日推荐