Collection Types in PL/SQL
Collection Types in PL/SQL
I often see questions on technical forums about arrays in PL/SQL, which type to use and what the
differences are. Although the documentation has quite a lot to say on the subject, I feel it tries to
introduce too many concepts at once and ends up being confusing, especially when it comes to
choosing between VARRAYs and Nested Table collections, where most of the "which type to use" advice
is about collection columns of database tables (which you probably don't need), and not about PL/SQL
at all. The documentation may give the impression that "array" means "VARRAY", without mentioning
the limitations of VARRAYs. This could be one reason why I often see VARRAYs used where Nested Table
collections would have been a better choice. I'm therefore going to set out what I consider the main
points of each type, from the point of view of a PL/SQL programmer.
Now although this is documented, and there are many articles and blogs that more or less repeat the
documentation (and unhelpfully invent new terms like "Associative Table"), I see two problems:
The documentation can be confusing, and I think it makes the wrong assumptions about what
you might be looking for.
It does not mention that VARRAYs lack some major functionality when compared with Nested
Tables in 10g.
Now that we have three types, the old term was (mostly) dropped from the documentation some years ago, and
none of them was really a table in the first place, it is better to use the newer termsCollection (for any of the
three types), Associative Array, Nested Table collection and (if you must) VARRAY.
There are three types of array? Which one do I use?
I'll begin by assuming you're writing some PL/SQL, and you're looking for some sort of variable to use in
your code that can hold multiple values and that you access using something like myarray[n].
I need to mention this because Oracle also supports the use of collection types as columns in database
tables, and the PL/SQL documentation attempts to describe both this feature and the PL/SQL
programming aspects at the same time, so for example when it talks about VARRAYs it sometimes
means VARRAY columns in the database, and sometimes VARRAY variables in PL/SQL. This can be
pretty confusing if you're a programmer looking for an array type and you find the documentation
talking about tables, columns, tablespaces, and VARRAYs being stored inline and useful when their
elements are usually all accessed in sequence (huh?) and so on. Now, if you really do want a collection
column in a database table (...which you probably don't...) then VARRAYs could well be just the thing.
If you're not, though, they aren't.
So, setting aside the whole idea of collection columns in database tables, and just looking at PL/SQL
programming, here are what I see as the main differences.
Before we go into that, however, it's worth mentioning the difference between creating a type in
SQL and declaring a type in PL/SQL.
Type created.
FROM TABLE(VARCHAR2_TT('Apple','Banana','Apricot'))
VAL
--------------------
Banana
1 row selected.
We created a collection type and used it in a query, all in SQL. Types created in this way can be used
in any query and any PL/SQL program (for other user accounts to use them, you need to GRANT EXECUTE
ON type_name TO other_account).
DECLARE
( batch_step batch_log.step_name%TYPE
, rows_processed PLS_INTEGER );
However, since they exist only in PL/SQL, SQL doesn't know about them and can't use them, and so you
can't use them in queries. In the earlier example, if we had declared VARCHAR2_TT within a PL/SQL
package, we could not use it in a query:
CREATE OR REPLACE PACKAGE testtypes AS
END testtypes;
Package created.
FROM TABLE(testtypes.VARCHAR2_TT('Apple','Banana','Apricot'))
FROM TABLE(testtypes.VARCHAR2_TT('Apple','Banana','Apricot'))
ERROR at line 2:
The SQL query is exactly the same as the previous example, except that we attempted to
use testtypes.VARCHAR2_TT (a type declared in a PL/SQL package) instead of VARCHAR2_TT (a type
declared in SQL). What the error message is saying there is that testtypes.VARCHAR2_TT is not a nested
table type it knows about, so the TABLE() expression is not valid. Of course, it is a nested table type
within PL/SQL, but SQL doesn't know PL/SQL.
Like any database object, you can query the data dictionary to see what collection types are available.
Here's a query I find useful:
SELECT ct.owner, ct.type_name, ct.elem_type_name, ct.length
FROM all_coll_types ct
, all_types ot
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;
Associative Arrays don't have constructors. This means you can't use the syntax myarray :=
mytype(2,4,6,37) to populate your collection with four values at once, as you can with the other types.
Although this may seem an invonvenient limitation, it's consistent with their purpose as a means of
associating pairs of values. Since the index value is significant (44 = 'UK', 380 = 'Ukraine' etc), it would
be wrong for the PL/SQL compiler to just default them sequentially as 1, 2, 3 and so on.
PL/SQL Declared only in PL/SQL code - no "CREATE OR REPLACE TYPE". SQL Associative Array
doesn't know anything about them.
SQL and PL/SQ Declared either in PL/SQL code or with "CREATE OR REPLACE TYPE". Nested Table
L
Must be initialised before use, e.g. myarray mytype := mytype();
VARRAY
Now this is doubtless true, depending on what other languages you are used to, but if (like me) you've
never come across sets or bags before, or perhaps you have used arrays in a language like JavaScript or
Korn Shell that doesn't make this kind of fancy distinction, and you just want some sort of multi-valued
variable to use in PL/SQL code, the above statements might make you think a VARRAY is probably what
you want (it's an array, right?) when it is not.
VARRAYs lack some of the functionality you get with nested table collections in 10g. The following
work only with nested table collections, not with VARRAYs.
my_array VARCHAR2_TT :=
VARCHAR2_TT('Apple','Apple','Orange','Banana');
BEGIN
ELSE
END IF;
END;
DECLARE
my_array VARCHAR2_TT :=
VARCHAR2_TT('Apple','Apple','Orange','Banana');
BEGIN
ELSE
END IF;
END;
DECLARE
my_array1 VARCHAR2_TT :=
VARCHAR2_TT('Apple','Orange','Cherry','Banana');
my_array2 VARCHAR2_TT :=
VARCHAR2_TT('Orange','Kumquat','Grape','Banana');
BEGIN
END LOOP;
END;
Orange
Banana
That could save you a lot of code if you want to check whether a collection's values are unique, or
whether a particular value exists in the collection, or to find the values common to two collections. As
you'll see from the links above, these are just a few of the useful collection features to explore.
Now let's try the same thing with a VARRAY instead of a nested table collection:
CREATE TYPE varchar2_vtt AS VARRAY(100) OF VARCHAR2(100)
Type created.
DECLARE
my_array VARCHAR2_VTT :=
VARCHAR2_VTT('Apple','Apple','Orange','Banana');
BEGIN
ELSE
END IF;
END;
*
ERROR at line 5:
And so on. In fact, none of the 10g collection functions, multiset conditions or operators work with
VARRAYs. That is a huge amount of useful functionality to lose just because you chose the wrong
collection type.
Summary
I have deliberately not covered the use of collection types as columns of database tables or views,
because I wanted to focus on PL/SQL programming, and a whole discussion of database table design
would just would have complicated things. The PL/SQL documentation attempts to cover collection
columns as well as PL/SQL collection variables in the same section, and in my opinion this causes
confusion.3 VARRAYs could well come into their own as a database column type for small lists of values
- although whether it is ever a good idea to use this approach is another question.
VARRAYs are generally only useful when you are working with actual VARRAY columns of database
tables, or when the LIMIT attribute is overwhelmingly useful in enforcing some business rule. Otherwise
they are just a functionally crippled version of nested table collections, with a LIMIT clause you don't
need.
Further reading
Collection extensions in 10g - oracle-developer.net
Using PL/SQL Collections and Records - Oracle 10g PL/SQL User's Guide and Reference
Using PL/SQL With Object Types - Oracle 10g PL/SQL User's Guide and Reference