集合是指在一个程序变量中包含多个值。
PL/SQL
提供的集合类型如下:
Associative Array:
|
TYPE t IS TABLE OF something INDEX BY PLS_INTEGER;
|
Nested Table:
|
TYPE t IS TABLE OF something;
|
VARRAY
:
|
TYPE t IS VARRAY (123) OF something;
|
三种集合的各自使用范围
前提是在
PL/SQL
中希望某种类型的多个值,并且希望使用
myarray[n]
的形式访问。
范围:SQL or
PL/SQL
最大的区别是首先需要确定使用范围,SQL
和PL/SQL
时两种不同的语言。PL/SQL
是SQL
的过程性语言。Associative Arrays
仅存在于PL/SQL
中。因此:
・
只能在PL/SQL
中使用的是:Associative Array
;
・
可以在SQL
中使用:Nested Table, VARRAY
;
同时需要注意的是:在SQL
中创建一个类型和PL/SQL
中声明一个类型是不同的。
SQL
中创建的集合类型
集合是
SQL
的一部分,不需要到
PL/SQL
中就可以使用它们:
CREATE TYPE VARCHAR2_TT AS TABLE OF VARCHAR2(100)
/
SELECT column_value AS val
FROM TABLE(VARCHAR2_TT('Apple','Banana','Apricot'))
WHERE column_value NOT LIKE 'A%';
VAL
--------------------
Banana
在以上的例子中,我们创建了一个集合类型,并在查询中使用,完全在SQL
中。这种形式创建的类型可以在任何查询和PL/SQL
程序中使用。其他用户使用需要GRANT EXECUTE ON type_name TO other_account
授权。
PL/SQL
中声明的集合类型
在
PL/SQL
中,可以使用
SQL
中创建的集合类型,或者本地声明自己的类型。在
PL/SQL
中声明可以更加灵活的自定义包中专用的类型,允许声明他们使用
%TYPE
和
%ROWTYPE
引用特定的表的列,如
TABLE
OF emp.empno%TYPE
。不过这仅仅是
PL/SQL
中的特征,不能在
SQL
中使用,甚至可以定义自己的类型,然后生命那种类型的集合。如下:
DECLARE
TYPE stats_rec IS RECORD
( batch_step batch_log.step_name%TYPE
, elapsed_time INTERVAL DAY(0) TO SECOND(0)
, rows_processed PLS_INTEGER );
TYPE stats_tt IS TABLE OF stats_rec; -- a locally-defined nested table collection type
由于仅在PL/SQL
中使用, SQL
并不知道:
CREATE OR REPLACE PACKAGE testtypes AS
TYPE varchar2_tt IS TABLE OF VARCHAR2(100);
END testtypes;
/
Package created.
SELECT column_value AS val
FROM TABLE(testtypes.VARCHAR2_TT('Apple','Banana','Apricot'))
WHERE column_value NOT LIKE 'A%';
FROM TABLE(testtypes.VARCHAR2_TT('Apple','Banana','Apricot'))
*
ERROR at line 2:
ORA-22905: cannot access rows from a non-nested table item
可以使用以下查询查看所有类型:
SELECT ct.owner, ct.type_name, ct.elem_type_name, ct.length
FROM all_coll_types ct
, all_types ot
WHERE ct.coll_type = 'TABLE'
AND ot.type_name(+) = ct.elem_type_name
AND ot.owner(+) = ct.elem_type_owner
AND ot.type_name IS NULL
ORDER BY ct.owner, ct.type_name;
Associative Arrays
特征
如果在
PL/SQL
中只需要声明和填充,那么关联数组将是一种方便的方法。不需要独立的
CREATE OR REPLACE TYPE
,不需要初始化,不需要扩展,可以随意的放入各种值,如下:
DECLARE
TYPE number_index_by_string IS TABLE OF NUMBER INDEX BY VARCHAR2(30);
TYPE string_index_by_number IS TABLE OF dept.loc%TYPE INDEX BY PLS_INTEGER;
v_country_codes NUMBER_INDEX_BY_STRING;
v_countries STRING_INDEX_BY_NUMBER;
BEGIN
v_country_codes('Ukraine') := 380;
v_country_codes('UAE') := 971;
v_country_codes('UK') := 44;
v_country_codes('USA') := 1;
v_countries(380) := 'Ukraine';
v_countries(971) := 'UAE';
v_countries(44) := 'UK';
v_countries(1) := 'USA';
END;
需要注意的是,在number_index_by_string
中使用了INDEX BY VARCHAR2(30)
,这使我们通过v_country_codes('UK')
引用数组的UK
元素。这是数组的唯一特征。关联数组没有构造体。也就是不能使用myarray := mytype(2,4,6,37)
进行一次性填充四个值。
PL/SQL
集合类型相对于SQL
集合类型
可以在
PL/SQL
中声明任何类型。
主要区别如下:
范围
|
意义
|
集合类型
|
PL/SQL
|
只能在
PL/SQL
中声明,没有
"CREATE OR REPLACE TYPE". SQL
中不能使用。
不需要初始化和扩展,只需要分配值给特定的元素,甚至不需要元素顺序。
可以选择
"index by" - PLS_INTEGER, BINARY_INTEGER
或
VARCHAR2.
没有构造体,必须显示分配值。不能在查询中作为一个表处理,如
SELECT * FROM TABLE(myarray)
|
Associative Array
|
SQL ,PL/SQL
|
可以在
PL/SQL
中声明或者使用
"CREATE OR REPLACE TYPE"
;
使用前必须初始化:
myarray mytype := mytype();
具有构造体,可以使用
mytype('x','y','z');
分配值;
必须进行扩展以增加元素,
myarray.EXTEND;
在查询中可以作为表处理
SELECT * FROM TABLE(myarray) (
如果使用
CREATE TYPE
创建
).
|
Nested Table
|
VARRAY
|
VARRAYs
的缺点
可以看到,
VARRAYs
和
Nested Tables
的描述基本相同,并且两种类型都可以在
SQL
和
PL/SQL
的
TABLE()
中使用。
VARRAYs
的一些不具有的特征在
nested table
中可以使用的包括:
・
集合函数,如CARDINALITY
,COLLECT
,POWERMULTISET
,等;
・
多重集条件,如IS A SET, MEMBER OF
,SUBMULTISET
;
・
多重集操作符,如MULTISET INTERSECT
;
如:
DECLARE
my_array VARCHAR2_TT :=
VARCHAR2_TT('Apple','Apple','Orange','Banana');
BEGIN
IF my_array IS A SET THEN
DBMS_OUTPUT.PUT_LINE('No duplicates found');
ELSE
DBMS_OUTPUT.PUT_LINE('Collection contains duplicates');
END IF;
END;
Collection contains duplicates
再如:
DECLARE
my_array VARCHAR2_TT :=
VARCHAR2_TT('Apple','Apple','Orange','Banana');
BEGIN
IF 'Orange' MEMBER OF my_array THEN
DBMS_OUTPUT.PUT_LINE('"Orange" exists in the collection');
ELSE
DBMS_OUTPUT.PUT_LINE('"Orange" does not exist in the collection');
END IF;
END;
"Orange" exists in the collection
DECLARE
my_array1 VARCHAR2_TT :=
VARCHAR2_TT('Apple','Orange','Cherry','Banana');
my_array2 VARCHAR2_TT :=
VARCHAR2_TT('Orange','Kumquat','Grape','Banana');
my_array3 VARCHAR2_TT := my_array1 MULTISET INTERSECT my_array2;
BEGIN
FOR i IN my_array3.FIRST..my_array3.LAST LOOP
DBMS_OUTPUT.PUT_LINE(my_array3(i));
END LOOP;
END;