0% found this document useful (0 votes)
143 views

Collection Types in PL/SQL

PL/SQL has three collection types: associative arrays, nested tables, and VARRAYs. Associative arrays can only be used in PL/SQL, while nested tables and VARRAYs can also be used in SQL. The main differences are that associative arrays allow non-sequential indexing while nested tables and VARRAYs require sequential indexing, and VARRAYs have limitations compared to nested tables introduced in Oracle 10g. Choosing a collection type depends on whether you need to use it in SQL versus only PL/SQL, and whether non-sequential versus sequential indexing is required.

Uploaded by

camjole
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
143 views

Collection Types in PL/SQL

PL/SQL has three collection types: associative arrays, nested tables, and VARRAYs. Associative arrays can only be used in PL/SQL, while nested tables and VARRAYs can also be used in SQL. The main differences are that associative arrays allow non-sequential indexing while nested tables and VARRAYs require sequential indexing, and VARRAYs have limitations compared to nested tables introduced in Oracle 10g. Choosing a collection type depends on whether you need to use it in SQL versus only PL/SQL, and whether non-sequential versus sequential indexing is required.

Uploaded by

camjole
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 16

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.

PL/SQL has three types of array


Or as they are known in PL/SQL, collection. "Collection" means a program variable containing more
than one value. The word "array" has a more specific meaning depending on your programming
language and computer science background. According to the PL/SQL documentation at least, other
languages provide arrays, sets, bags, linked lists and hash tables. The equivalent types in PL/SQL can
all be referred to as "collections", and PL/SQL provides three of them:

Associative TYPE t IS TABLE OF something INDEX BY PLS_INTEGER;


Array:

Nested Table: TYPE t IS TABLE OF something;

VARRAY: TYPE t IS VARRAY (123) OF something;

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.

Using the new names


At first there was only one type of array, and it was called a PL/SQL Table. The idea was presumably that although
it was an array structure in program memory, we could still think of it as being rather like a database table, as it
had rows and a column. However, it was never really very much like a database table, and when more types were
added in later versions the name changed.

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.

Scope: SQL or PL/SQL


The biggest difference is to do with where you can use them. SQL and PL/SQL are two separate
languages, with PL/SQL the procedural wrapper for SQL. 1 Associative Arrays exist only in PL/SQL, and
SQL doesn't know anything about them. So, the first distinction to make is between the one PL/SQL-
only type and the other two:

 Only in PL/SQL: Associative Array

 Can also use in SQL: Nested Table, VARRAY

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.

Collection types created in SQL


Collections are part of the SQL language in Oracle, so you use them without even going into PL/SQL:
CREATE TYPE VARCHAR2_TT AS TABLE OF VARCHAR2(100)

Type created.

SELECT column_value AS val

FROM TABLE(VARCHAR2_TT('Apple','Banana','Apricot'))

WHERE column_value NOT LIKE 'A%';

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).

Collection types declared in PL/SQL


Within your PL/SQL programs, you can either use existing types that were created in SQL (like
the VARCHAR2_TT example above) or you can declare your own types locally. Declaring them within
PL/SQL gives you a lot of flexibility to define custom types for use only within a particular procedure or
package, and also lets you anchor them to a particular table or column's definition using the %TYPE and
%ROWTYPE syntax (for example, TABLE OF emp.empno%TYPE will take its definition from emp.empno,
whatever that is at the time you compile the PL/SQL code - this is a PL/SQL feature not available
within SQL). You can even declare your own record types and then declare a collection of that record
type, for example:

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

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

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

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

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;

OWNER TYPE_NAME ELEM_TYPE_NAME


LENGTH

-------------------- -------------------------- -------------------------


---------

DMSYS DMCLAOS DMCLAO

DMSYS DMCLBOS DMCLBO

DMSYS DM_ITEMS VARCHAR2


4000

DMSYS ORA_MINING_NUMBER_NT NUMBER

DMSYS ORA_MINING_VARCHAR2_NT VARCHAR2


4000

EXFSYS RLM$DATEVAL TIMESTAMP


EXFSYS RLM$KEYVAL VARCHAR2
1000

EXFSYS RLM$NUMVAL NUMBER

EXFSYS RLM$ROWIDTAB VARCHAR2


38

MDSYS RDF_MODELS VARCHAR2


25

MDSYS RDF_RULEBASES VARCHAR2


25

MDSYS SDO_NUMTAB NUMBER

MDSYS SDO_TOPO_NSTD_TBL NUMBER

SYS DBMS_AW$_COLUMNLIST_T VARCHAR2


100

SYS DBMS_DEBUG_VC2COLL VARCHAR2


1000

SYS KU$_OBJNUMSET NUMBER

SYS KU$_VCNT VARCHAR2


4000

SYS ORA_DM_TREE_NODES ORA_DM_TREE_NODE

WILLIAM DATE_TT DATE

WILLIAM INTEGER_TT INTEGER

WILLIAM NUMBER_TT NUMBER

WILLIAM SUDOKU_CELL_TT NUMBER

WILLIAM VARCHAR2_TT VARCHAR2


4000
WMSYS WM$LOCK_TABLE_TYPE WM$LOCK_INFO_TYPE

Notice that SYS.DBMS_DEBUG_VC2COLL is a VARCHAR2(1000) collection, which can be handy in an


environment where you cannot create your own new types.

Special Features of Associative Arrays


Associative Arrays are convenient if you just need a declare-and-go type to use within some PL/SQL
code. No separate CREATE OR REPLACE TYPE to put through release control, no initialising, no extending,
and you can just put values anywhere you like in them:
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;

Notice also we can INDEX BY a character string. In the example above, we


declared number_index_by_string using INDEX BY VARCHAR2(30), which means we can refer to its 'UK'
element asv_country_codes('UK'). This is a unique feature of Associative Arrays. (In fact it's the point
of them - they associate pairs of values such as 'UK' and 44. In the other collection types the index is
just a locator and doesn't have any meaning itself.) You can use this to access array values by their
business key: for example, if we had a variable v_country VARCHAR2(10), we could find the
corresponding element in the array using v_country_codes(v_country). (We can also use the more usual
numeric approach, INDEX BY PLS_INTEGER.)

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.

SQL vs PL/SQL collection types: summary


You can declare any type you like within PL/SQL, and you get a lot of flexibility that way such as the
use of PL/SQL-only language features, but remember that SQL does not know PL/SQL, and so you
cannot use your PL/SQL types in SQL queries.

The other main differences are listed below.

Scope What that means Collection Types

PL/SQL Declared only in PL/SQL code - no "CREATE OR REPLACE TYPE". SQL Associative Array
doesn't know anything about them.

No initialisation or extending required - just assign values to any


arbitrary element, doesn't even have to be consecutive.

You can choose what to "index by"


- PLS_INTEGER, BINARY_INTEGER2 or VARCHAR2.

No constructor - you must assign values explicitly.

Can't treat as a table in queries, e.g. you cannot SELECT * FROM


TABLE(myarray)

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

Have constructors - you can assign values using


mytype('x','y','z');

Must be extended as required, e.g. myarray.EXTEND; to add each


array element.

Can treat as a table in queries e.g. SELECT * FROM


TABLE(myarray) (if created in SQL with CREATE TYPE).

So what's wrong with VARRAYs?


From reading the PL/SQL documentation, you might get the impression that VARRAYs and Nested
Tables are pretty similar. After all, you can use either type in SQL queries using TABLE() expressions as
well as in PL/SQL code, and most of the differences listed in the section Choosing Between Nested
Tables and Varrays are about storage considerations for collection columns of database tables (for
example, "Each varray is stored as a single object, either inside the table of which it is a column (if
the varray is less than 4KB) or outside the table but still in the same tablespace (if the varray is
greater than 4KB)") - none of which is relevant when choosing an array type for a PL/SQL program.

Worse, in my view, are the statements:

 Arrays in other languages become varrays in PL/SQL.

 Sets and bags in other languages become nested tables in PL/SQL.

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.

 Collection Functions such as CARDINALITY and SET

 Multiset Conditions such as IS A SET, MEMBER OF and SUBMULTISET

 Multiset Operators such as MULTISET INTERSECT

For example, try these:


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;

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

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;

IF my_array IS A SET THEN

*
ERROR at line 5:

ORA-06550: line 5, column 8:

PLS-00306: wrong number or types of arguments in call to 'IS A SET'

ORA-06550: line 5, column 5:

PL/SQL: Statement ignored

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

 Application Developer's Guide - Object-Relational Features  - Oracle 10g Documentation

You might also like