Collections
Collections Overview
Types of Collections
Associative arrays
他是同种类型的一维、无边界的稀疏集合,只能用于 PL/SQL
DECLARE TYPE t_name IS TABLE OF varchar2(10) INDEX BY PLS_INTEGER; --创建 Collection
i_name t_name; --创建 instance
l_row PLS_INTEGER;
BEGIN
i_name(202020):='aaa';
i_name(-125):='bbb';
i_name(88):='ccc'; --赋值,row number 可以为任何整数,且可以跳跃(稀疏的),也不用按照顺序赋值,其内部最终按照 row number 排序
l_row := i_name.FIRST; --返回第一个 row number
l_row := i_name.NEXT (l_row); --返回 l_row 之后的一个 row number,会自动跳过为空的行
DBMS_OUTPUT.put_line (i_name (l_row)); --返回指定行的值
END;
Nested tables
也是同种类型的一维无边界集合。起初他是密集的,经过删除操作会变成稀疏。他可以在 PL/SQL 和 Database(某一列为一个嵌套表)中被定义。他是 multisets 的,也就是说 nested table 中的元素没有内在的顺序
DECLARE TYPE t_name IS TABLE OF varchar2(10); --创建 Collection,和 Associative arrays 相差一个 INDEX BY
i_n1 t_name := t_name(); --创建 instance,需要使用构造函数
i_n2 t_name := t_name();
i_n3 t_name := t_name();
BEGIN
i_n1.EXTEND(2); --赋值前要分配空间
i_n1(1):='aaa'; --赋值,row number 最小为 1,最大为 EXTEND 分配的空间数,可以跳过某个行不分配则为 null
i_n1(2):='bbb';
i_n2.EXTEND; --分配一个空间
i_n2(1):='bbb';
i_n3 := i_n1 MULTISET EXCEPT i_n2; --10g 开始提供的功能,将 i_n1 中除去 i_n2 的值,赋值给 i_n3
FOR l_row IN i_n1.FIRST .. i_n1.LAST --从行首到行尾,依次输出
LOOP
DBMS_OUTPUT.put_line (i_n1(l_row));
END LOOP;
END;
VARRAYs
VARRAYs (variable-sized arrays),也是同种类型的一维集合。但他是有界且不稀疏的。在定义 VARRAYs 时要指定他的最大范围。VARRAYs 也可以在 PL/SQL 和 Database 中被定义,但他其中的元素是有顺序的
DECLARE TYPE t_name IS VARRAY (2) OF VARCHAR2 (10); --创建 Collection,需要指定最大范围
i_n1 t_name := t_name(); --创建 instance,需要使用构造函数
BEGIN
i_n1.EXTEND(2); --赋值前要分配空间,不能超过最大范围
i_n1(1):='aaa'; --赋值,row number 最小为 1,最大为 EXTEND 分配的空间数
i_n1(2):='bbb';
END;
Where You Can Use Collections
作为 record 的成分
作为程序的参数
由于 Oracle 没有预定的集合类型,当作为参数使用前,需要定义集合类型:
1.用 CREATE TYPE 定义 schema-level 类型
2.在 package specification 中声明
3.在外层作用域定义
作为函数的返回值
1.返回集合直接赋值给 collection variable,这时这个 collection variable 不需要初始化
2.将返回集合中的一个元素赋值给一个类型兼容的变量
variable_of_element_type := function() (subscript);
如果函数返回空值,在赋值时会产生 COLLECTION_IS_NULL 异常,应该捕获并适当处理该异常
作为数据库表的列
当使用 nested table datatype 作为列时,必须指定 store table 的名字:
CREATE TABLE personality_inventory (
person_id NUMBER,
favorite_colors Color_tab_t,
date_tested DATE,
test_results BLOB)
NESTED TABLE favorite_colors STORE AS favorite_colors_st;
不能对 store table 进行维护或者试图直接查询、存储数据,只能通过 outer table 来获取他的属性。也不能指定他的 storage parameters,他继承与 outermost table。
nested tables 和 VARRAYs 的区别在于,VARRAY 和其他数据一起存于表内,而 nested table 存于表外,VARRAY 适合 "small" arrays,nested table 适合 "large" arrays。
作为 object type 的属性
Choosing a Collection Type
1.如果要用稀疏 array,那么只能使用 associative array,虽然可以先分配 nested table 在删除其中的项目,但效率很低
2.如果在 PL/SQL 中要用负数的下标,只能用 associative array
3.如果使用 10g,希望使用 set 层面的操作,那么选择 nested tables + MULTISET EXCEPT 的方法
4.如果要限制存储的行数,使用 VARRAYs
5.如果要在 column 中存储大数据量集合,那么使用 nested table,Oracle可以使用单独的表来存储他
6.如果你想将存在 collection column 中的数据保持原有顺序,并且数据量很小,可以使用 VARRAY,小的概念可以按照 BLOCK 的大小来判断,如果数据量超过一个 BLOCK,将会产生行连接
7.还有些情况适合使用 VARRAY:you don't want to worry about deletions occurring in the middle of the data set; your data has an intrinsic upper bound; or you expect, in general, to retrieve the entire collection simultaneously.
Collection Methods (Built-Ins)
Collection methods 只能用于 PL/SQL,不能在 SQL 中使用
The COUNT Method
用于计算 associative array, nested table, or VARRAY 中元素的个数,使用 DELETEd or TRIMmed 将减少 COUNT
定义:
FUNCTION COUNT RETURN PLS_INTEGER;
对于初始化了,但不包含元素的 collection 返回 0,对于空的 associative array 也返回 0
如果对未初始化的 nested table or a VARRAY 使用 COUNT 将返回 COLLECTION_IS_NULL 异常,associative arrays 无需初始化,故不会返回异常
The DELETE Method
DELETE 用于删除 collection 中的一个、多个或者全部元素:
1.不加参数,删除所有元素,对于 VARRAYs 只能使用这种方法,因为 VARRAYs 不能使稀疏的,要删除一行,只能用 TRIM 删除最后一行
2.DELETE(i) 删除第 i 行
3.DELETE(i,j) 删除第 i~j 行
对于使用参数的 DELETE,其实使用一个占位符来代替删除的元素,以后还可以为他们再赋值,赋值之后在 COUNT 中又开始计数。从物理角度来考虑,PL/SQL 只有在删除足够多数量的元素,以至于可以释放 entire page of memory 时才释放内存,但不加参数的 DELETE 是立即释放所有内存的
定义:
PROCEDURE DELETE;
PROCEDURE DELETE (i [BINARY_INTEGER | VARCHAR2(size_limit)]);
PROCEDURE DELETE (i [BINARY_INTEGER | VARCHAR2(size_limit)],
j [BINARY_INTEGER | VARCHAR2(size_limit)]);
如果 i,j 超出范围,不会出现异常,只是删除在范围内的,超出部分忽略
如果对未初始化的 nested table or a VARRAY 使用 DELETE 将返回 COLLECTION_IS_NULL 异常
The EXISTS Method
判断 collection 中的指定行是否存在,如果以前存在,后来被 DELETE 了,那也返回 false
定义:
FUNCTION EXISTS (i IN [BINARY_INTEGER | VARCHAR2(size_limit)]) RETURN BOOLEAN;
The EXTEND Method
用于为 nested table or VARRAY 分配空间
1.不加参数,分配一个 null element
2.EXTEND(n),分配 n 个 null element
3.EXTEND(n,i),分配 n 个和第 i 值相同的 element,这对于有 NOT NULL 限制的 element 是必要的
定义:
PROCEDURE EXTEND (n PLS_INTEGER:=1);
PROCEDURE EXTEND (n PLS_INTEGER, i PLS_INTEGER);
如果用 DELETEd or TRIMmed 删除了 collection 的最后一个元素,再 EXTEND 时,会跳过这个,在后面分配空间
如果对未初始化的 nested table or a VARRAY 使用 DELETE 将返回 COLLECTION_IS_NULL 异常,如果 EXTEND 超过 VARRAY 的最大限制,将返回 SUBSCRIPT_BEYOND_LIMIT 异常
The FIRST and LAST Methods
分别返回 COLLECTION 中可用的最低和最高下标,被 DELETE 的不会显示
定义:
FUNCTION FIRST RETURN PLS_INTEGER;
FUNCTION LAST RETURN PLS_INTEGER;
对于初始化了,但没有元素的 COLLECTION,都返回 null;对于至少有一个 element 的 VARRAYs,FIRST 总为 1,LAST 总等于 COUNT
如果对未初始化的 nested table or a VARRAY 使用 FIRST and LAST 将返回 COLLECTION_IS_NULL 异常
The LIMIT Method
返回 VARRAY 的最大数目限制,如果用于 nested tables or to associative arrays 将返回 NULL
定义:
FUNCTION LIMIT RETURN PLS_INTEGER;
如果对未初始化的 nested table or a VARRAY 使用 LIMIT 将返回 COLLECTION_IS_NULL 异常
The PRIOR and NEXT Methods
用于遍历 COLLECTION 中的内容
定义:
FUNCTION PRIOR (i [BINARY_INTEGER | VARCHAR2(size_limit)])
RETURN [BINARY_INTEGER | VARCHAR2(size_limit)];
FUNCTION NEXT (i [BINARY_INTEGER | VARCHAR2(size_limit)])
RETURN [BINARY_INTEGER | VARCHAR2(size_limit)];
如果应用于初始化了但为空的 COLLECTION 时,返回 null;如果 i 大于等于 COUNT,则 NEXT 返回 null;如果 i 小于等于 FIRST,则 PRIOR 返回 null
就目前来说,如果 i 大于 COUNT,则 PRIOR 返回 LIMIT;如果 i 小于 FIRST,则 NEXT 返回 LIMIT,但以后的版本不知道是不是这样了
如果对未初始化的 nested table or a VARRAY 使用 PRIOR and NEXT 将返回 COLLECTION_IS_NULL 异常
The TRIM Method
由于删除 nested table or VARRAY 末尾的 n 行,如果不加参数,删除最后一行,如果用于 associative array 将产生编译时错误
DELETE and TRIM 联合使用将产生重复删除的问题,比如 DELETE 删除最后一行之后,再用 TRIM 删除最后 n 行时,其实 TRIM 重复操作了被 DELETE 删除的那行(因为删除之后变成了占位符),使得实际上少删除一行,因此 Oracle 建议两者不要混合使用
定义:
PROCEDURE TRIM (n PLS_INTEGER:=1);
如果 n 为 null,TRIM 不做任何事情
如果对未初始化的 nested table or a VARRAY 使用 TRIM 将返回 COLLECTION_IS_NULL 异常
如果 TRIM 的 n 大于实际存在的元素个数,那么返回 SUBSCRIPT_BEYOND_COUNT 异常
Working with Collections
Declaring Collection Types
两种声明方法:
1.在 PL/SQL 中使用 TYPE 语句,如果定义在 package 中,对于使用该类型的用户要将 package 的 EXECUTE 权限授予该用户
2.对于 nested table type or VARRAY type 在 schema-level 使用 CREATE TYPE 命令,并将该类型的 EXECUTE 权限授予需要使用的用户
Declaring an associative array collection type
TYPE table_type_name IS TABLE OF datatype [ NOT NULL ]
INDEX BY index_type;
datatype 包括:
1.Scalar datatype:VARCHAR2, CLOB, POSITIVE, DATE, or BOOLEAN 等等
2.Anchored datatype:使用 %TYPE、%ROWTYPE
3.Complex datatype:9i R2 开始可以使用 object types and collection types
index_type 包括:
INDEX BY BINARY_INTEGER;(9i R2 之前只能使用该类型)
INDEX BY PLS_INTEGER;
INDEX BY POSITIVE;
INDEX BY NATURAL;
INDEX BY SIGNTYPE;
INDEX BY VARCHAR2(32767);
INDEX BY table.column%TYPE;
INDEX BY cursor.column%TYPE;
INDEX BY package.variable%TYPE;
INDEX BY package.subtype;
Declaring a nested table or VARRAY
CREATE [ OR REPLACE ] TYPE type_name AS | IS
TABLE OF element_datatype [ NOT NULL ];
CREATE [ OR REPLACE ] TYPE type_name AS | IS
VARRAY (max_elements) OF element_datatype [ NOT NULL ];
DROP TYPE type_name [ FORCE ];
element_datatype 包括:大部分 scalar datatypes, an object type, or a REF object type,如果是 object type,那么他的属性不能是 collection 类型;在 PL/SQL 中,如果是 RECORD,那么他只能包含 scalars or objects 类型
FORCE:即使该 collection type 被其他类型引用,也 drop 他
Changing nested table or VARRAY characteristics
1.改变 VARRAY 元素最大个数:
ALTER TYPE list_t MODIFY LIMIT 100 INVALIDATE | CASCADE
2.改变元素类型为 variable character, RAW, or numeric 的类型长度或精度
CREATE TYPE list_t AS VARRAY(10) OF VARCHAR2(80);
ALTER TYPE list_t MODIFY ELEMENT TYPE VARCHAR2(100) INVALIDATE | CASCADE
INVALIDATE:所有使用该类型的对象失效
CASCADE:将改变传递给所有使用该类型的对象
Declaring and Initializing Collection Variables
collection_name collection_type [:= collection_type (...)];
对于 nested table or VARRAY 需要用构造函数初始化,构造函数可以可以接受用逗号分割的初始值作为参数,如果不初始化就使用会得到错误:ORA-06531: Reference to uninitialized collection
Initializing implicitly during direct assignment
同一 collection type (必须同一个类型,哪怕元素类型相同的不同 collection type 也不行)的不同变量,可以通过直接赋值进行隐式初始化
DECLARE
earth_colors Color_tab_t := Color_tab_t('BRICK', 'RUST', 'DIRT');
wedding_colors Color_tab_t; --必须同一类型,都是 Color_tab_t,这里没有初始化
BEGIN
wedding_colors := earth_colors; --赋值时同时初始化
wedding_colors(3) := 'CANVAS';
END;
Initializing implicitly via FETCH
如果表中的列是 Collection type,那么可以像一般类型一样通过 select ... into、FETCH 直接将值传递给相同类型的变量,并可以自动给该变量初始化
Populating Collections with Data
Using the assignment operator
1.单个赋值:
countdown_test_list (43) := 'Internal pressure';
2.整个 record (结构要一致)赋值:
DECLARE
TYPE emp_copy_t IS TABLE OF emp%ROWTYPE;
l_emps emp_copy_t := emp_copy_t( );
l_emprec emp%ROWTYPE;
BEGIN
l_emprec.ename := 'Steven';
l_emprec.salary := 10000;
l_emps.EXTEND
l_emps (l_emps.LAST) := l_emprec;
END;
相同的还可以使用 select ... into、cursor FOR loop、select ... BULK COLLECT into(9i R2 之后,可将整个表赋值给 Collection,不需要初始化) 进行赋值
3.相同类型的 Collection 直接赋值(不需要初始化)
Accessing Data Inside a Collection
需要注意几点:
1.对于稀疏 Collection,当访问不存在的元素时会产生 NO_DATA_FOUND 异常
2.访问超出 EXTEND 分配空间的元素时产生 ORA-06533: Subscript beyond count 异常
3.对于 VARRAY,访问大于最大元素个数的元素时产生 ORA-06532: Subscript outside of limit 异常
Collections of Complex Datatypes
Collections of records
可以定义元素类型为 record 的 collection,使用 %ROWTYPE or a programmer-defined record type。适用于 PL/SQL,如果是定义在表的列上,不能使用 %ROWTYPE
Multilevel collections
9i R2 开始可以定义 collection 的元素类型也为 collection,既 collection 嵌套,被称为 multilevel collections,通过这种方法,可以构造多维 collection。
Using String-Indexed Collections
9i R2 开始可以使用 strings 作为 index type,FIRST, LAST, PRIOR, and NEXT 方法返回的也是 strings
Working with Collections in SQL
Oracle8 开始提供 collection pseudo-functions,他们可以使数据表和 collection 的数据互相转换,只能用于 SQL 语句中
1.The CAST pseudo-function
在 SQL 中,用于将 built-in datatype or collection type 转换为另一种 built-in datatype or collection type
CREATE TYPE Color_nt AS TABLE OF VARCHAR2(30);
CREATE TYPE Color_vat AS VARRAY(16) OF VARCHAR2(30);
CREATE TABLE color_models (
model_type VARCHAR2(12),
colors Color_vat);
SELECT COLUMN_VALUE FROM
TABLE (SELECT CAST(colors AS Color_nt)
FROM color_models
WHERE model_type = 'FGB');
2.The MULTISET pseudo-function
MULTISET 必须和 CASTs 一起使用,MULTISET 将数据集转换为 collection,SQL MULTISET function 和操作 nested tables 的 PL/SQL MULTISET 是完全不同的
SELECT CAST (MULTISET (SELECT field FROM table) AS collection-type)
FROM DUAL;
As with the CAST pseudo-function, MULTISET cannot serve as the target of an INSERT, UPDATE, or DELETE statement.
3.The TABLE pseudo-function
TABLE 将 collection 转换为可以 SELECT from 的结果集,既将 collection 中的内容像数据表一样 select 出来
例如,表中含有 collection 字段,现在要找出 collection 中包含某个内容的所有数据:
SELECT *
FROM color_models c
WHERE 'RED' IN
(SELECT COLUMN_VALUE FROM TABLE(c.colors));
MODEL_TYPE COLORS
------------ ------------------------------------------------------
RGB COLOR_TAB_T('RED', 'GREEN', 'BLUE')
COLUMN_VALUE 是 TABLE operator 创建的 system-defined 列名
Nested Table Multiset Operations
10g 之后针对 nested tables 提供了 high-level set operations,将其作为 multisets 对待
Operation Return value Description
= BOOLEAN 比较两个 nested tables,如果 named type、cardinality、elements 相等,返回 TRUE,如果 nested table 中包含 null 则必不相等。10g 之前要比较,只能通过一个个元素来比较
<> or != BOOLEAN 比较两个 nested tables 是否不同
[NOT] IN ( ) BOOLEAN 判断左边的 nested table 是否在右边括号中的 nested tables list 中
x MULTISET EXCEPT NESTED TABLE 返回一个 NESTED TABLE,他的元素在 x 中,但不在 y 中,所有的 NESTED TABLE 都必须是相同类型的。DISTINCT 表示返回的 nested table 中除去重复元素
[DISTINCT] y
x MULTISET INTERSECT NESTED TABLE 返回一个 NESTED TABLE,他的元素是 x 和 y 中元素的交集
[DISTINCT] y
x MULTISET UNION NESTED TABLE 返回一个 NESTED TABLE,他的元素是 x 和 y 中元素的并集
[DISTINCT] y
SET(x) NESTED TABLE 返回一个 NESTED TABLE,包含 x 中不重复的所有元素
x IS [NOT] A SET BOOLEAN 判断 x 中的元素是否有重复值
x IS [NOT] EMPTY BOOLEAN 判断 x 是否为空
e [NOT] MEMBER BOOLEAN 判断表达式 e 是否包含在 x 的元素中
[OF] x
y [NOT] SUBMULTISET BOOLEAN 判断 y 的元素是否全都包含在 x 的元素中
[OF] x
Maintaining Schema-Level Collections
Necessary Privileges
如果创建 Schema-Level 的 nested tables and VARRAYS,其他 Schema 如果需要使用该类型必须有该类型的 EXECUTE 权限:
GRANT EXECUTE on Color_tab_t TO JOE;
对于包含 Schema-Level Collections 的 packages, procedures, functions 只要有这些过程的 EXECUTE 权限就可以使用这些类型了,但对于 PL/SQL anonymous blocks 中使用这些类型,只能直接将这些类型的 EXECUTE 权限授予执行的用户
如果某表的一列为 Schema-Level Collection 类型,对该表要有 SELECT, INSERT, UDPATE, and DELETE 权限才能做相应的操作,其次在 INSERT or UPDATE 时还需要有该 Collection 类型的 EXECUTE 权限
Collections and the Data Dictionary
1.USER_TYPES
所有创建的类型
SELECT type_name FROM user_types
WHERE typecode ='COLLECTION';
2.USER_SOURCE
类型的定义
SELECT text FROM user_source
WHERE name = 'FOO_T' AND type = 'TYPE'
ORDER BY line;
3.USER_DEPENDENCIES
依赖创建的类型的对象
SELECT name, type FROM user_dependencies
WHERE referenced_name='FOO_T';