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

Collection

The document discusses different types of collections in Oracle including associative arrays, nested tables, and VARRAYs. It provides examples of how each collection type can be declared and used. The key differences between the collection types are summarized in a table, including whether they can be used in SQL, their dimensionality, initialization, indexing method, and other characteristics.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
78 views

Collection

The document discusses different types of collections in Oracle including associative arrays, nested tables, and VARRAYs. It provides examples of how each collection type can be declared and used. The key differences between the collection types are summarized in a table, including whether they can be used in SQL, their dimensionality, initialization, indexing method, and other characteristics.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 13

Collection

Unbounded vs bounded collection


A collection is said to be bounded if there is pre defined limit on number of elements in the
collection
it is unbounded if there is not upper or lower limit on number of element in collection.
Sparce vs Dense
A collection is sparce if the all rows between first and last row are defined and given values
to each row.
A collection is dense if some rows are not defined and values are not populated sequentially
i.e. there gap exist between the rows of collection.
-- Index by string is only supported for Associative array.
Collection Examples
This section provides relatively simple examples of each different type of collection with
explanations of the major characteristics.
Using an associative array
In the following example, I declare an associative array type and then a collection based on
that type. I populate it with four rows of data and then iterate through the collection,
displaying the strings in the collection. A more thorough explanation appears in the table
after the code:

DECLARE

TYPE list_of_names_t IS TABLE OF person.first_name%TYPE


INDEX BY PLS_INTEGER;

happyfamily list_of_names_t;
l_row PLS_INTEGER;

BEGIN

happyfamily (2020202020) := 'Eli';


happyfamily (-15070) := 'Steven';
happyfamily (-90900) := 'Chris';
happyfamily (88) := 'Veva';

l_row := happyfamily.FIRST;

WHILE (l_row IS NOT NULL)


LOOP
DBMS_OUTPUT.put_line (happyfamily (l_row));
l_row := happyfamily.NEXT (l_row);
END LOOP;

END;
Using a nested table
In the following example, I first declare a nested table type as a schema-level type. In my
PL/SQL block, I declare three nested tables based on that type. I put the names of everyone in
my family into the happyfamily nested table. I put the names of my children in the children
nested table. I then use the set operator, MULTISET EXCEPT (introduced in Oracle
Database 10g), to extract just the parents from the happyfamily nested table; finally, I display
the names of the parents. A more thorough explanation appears in the table after the code:

CREATE TYPE list_of_names_t IS TABLE OF VARCHAR2 (100)


DECLARE

happyfamily list_of_names_t := list_of_names_t ();


children list_of_names_t := list_of_names_t ();
parents list_of_names_t := list_of_names_t ();

BEGIN

happyfamily.EXTEND (4);
happyfamily (1) := 'Eli';
happyfamily (2) := 'Steven';
happyfamily (3) := 'Chris';
happyfamily (4) := 'Veva';

children.EXTEND;
children (1) := 'Chris';
children.EXTEND;
children (2) := 'Eli';

parents := happyfamily MULTISET EXCEPT children;

FOR l_row IN parents.FIRST .. parents.LAST


LOOP
DBMS_OUTPUT.put_line (parents (l_row));
END LOOP;
END;
Using a VARRAY
In the following example, I demonstrate the use of VARRAYs as columns in a relational
table. First, I declare two different schema-level VARRAY types. I then create a relational
table, family, that has two VARRAY columns. Finally, in my PL/SQL code, I populate two
local collections and then use them in an INSERT into the family table. A more thorough
explanation appears in the table after the code:
REM Section A SQL> CREATE TYPE first_names_t IS VARRAY (2) OF
VARCHAR2 (100); 2 / Type created. SQL> CREATE TYPE child_names_t IS
VARRAY (1) OF VARCHAR2 (100); 2 / Type created. REM Section B
SQL> CREATE TABLE family ( 2 surname VARCHAR2(1000) 3 ,
parent_names first_names_t 4 , children_names child_names_t 5 );
Table created. REM Section C SQL> 1 DECLARE 2 parents first_names_t :=
first_names_t (); 3 children child_names_t := child_names_t
(); 4 BEGIN 5 parents.EXTEND (2); 6 parents (1) :=
'Samuel'; 7 parents (2) := 'Charina';
8 -- 9 children.EXTEND; 10 children (1) := 'Feather'; 11
12 -- 13 INSERT INTO family 14 ( surname, parent_names,
children_names ) 15 VALUES ( 'Assurty', parents,
children ); 16 END; SQL> /

Choosing a Collection Type

 If your PL/SQL application requires negative subscripts, you also have to use
associative arrays.
 you’d find it useful to perform high-level set operations on your collections, choose
nested tables over associative arrays.
 If you want to preserve the order of elements stored in the collection column and if
your dataset will be small, use a VARRAY.

Table 12-2. Comparing Oracle collection types

Characteristic Associative array Nested table VARRAY

Dimensionality Single Single Single

Usable in SQL? No Yes Yes

Yes; data stored Yes; data stored


Usable as column
No “out of line” (in “inline” (in same
datatype in a table?
separate table) table)

Atomically null;
Atomically null;
Empty (cannot be null); illegal to
Uninitialized state illegal to reference
elements undefined reference
elements
elements

Automatic, when Via constructor, Via constructor,


Initialization
declared fetch, assignment fetch, assignment

BINARY_INTEGER
Positive integer Positive integer
(and any of its
Index type between 1 and between 1 and
subtypes) or
2,147,483,647 2,147,483,647
VARCHAR2
Characteristic Associative array Nested table VARRAY

Initially, no; after


Sparse? Yes No
deletions, yes

Bounded? No Can be extended Yes

No; may need to


Can assign value to EXTEND first,
No; may need to
any element at any Yes and cannot
EXTEND first
time? EXTEND past
upper bound

Use built-in
EXTEND
EXTEND (or
procedure (or
Means of Assign value to element TRIM), but only
TRIM to
extending with a new subscript up to declared
condense), with no
maximum size
predefined
maximum

Yes, Oracle
Can be compared
No Database 10g and No
for equality?
later

Can be Yes, Oracle


manipulated with No Database 10g and No
set operators later

Retains ordering
and subscripts
when stored in and N/A No Yes
retrieved from
database?

Collection Methods (Built-ins)


Table Collection methods

Method
(function or Description
procedure)

Returns the current number of elements in a collection. If elements


COUNT function have been DELETEd or TRIMmed from the collection, they are not
included in COUNT

DELETE Removes one or more elements from the collection. Reduces COUNT
procedure if the element is not already removed. With VARRAYs, you can
Method
(function or Description
procedure)

delete only the entire contents of the collection.

Returns TRUE or FALSE to indicate whether the specified element


EXISTS function
exists.

EXTEND Increases the number of elements in a nested table or VARRAY.


procedure Increases COUNT.

FIRST, LAST
Returns the smallest (FIRST) and largest (LAST) subscript in use.
functions

LIMIT function Returns the maximum number of elements allowed in a VARRAY.

Returns the subscript immediately before (PRIOR) or after (NEXT) a


PRIOR, NEXT specified subscript. You should always use PRIOR and NEXT to
functions traverse a collection, especially if you are working with sparse (or
potentially sparse) collections.

Removes collection elements from the end of the collection (highest


TRIM procedure
defined subscript).

Exception
 If COUNT is applied to an uninitialized nested table or a VARRAY, it raises the
COLLECTION_IS_NULL predefined exception. Note that this exception is not
possible for associative arrays, which do not require initialization.
 Delete Method - When applied to VARRAYs, you can issue DELETE only without
arguments (i.e., remove all rows). In other words, you cannot delete individual rows
of a VARRAY, possibly making it sparse. The only way to remove a row from a
VARRAY is to TRIM from the end of the collection
 If DELETE is applied to an uninitialized nested table or a VARRAY, it raises the
COLLECTION_ IS_NULL predefined exception
 If EXTEND is applied to an uninitialized nested table or a VARRAY, it raises the
COLLECTION_IS_NULL predefined exception. An attempt to EXTEND a
VARRAY beyond its declared limit raises the SUBSCRIPT_BEYOND_LIMIT
exception
 If FIRST and LAST are applied to an uninitialized nested table or a VARRAY, they
raise the COLLECTION_ IS_NULL predefined exception.
The PRIOR and NEXT Methods
PRIOR returns the next-lower index value in use relative to i; NEXT returns the next higher

FUNCTION compute_sum (the_list IN list_t) RETURN NUMBER


AS
row_index PLS_INTEGER := the_list.FIRST;
total NUMBER := 0;
BEGIN
LOOP
EXIT WHEN row_index IS NULL;
total := total + the_list(row_index);
row_index := the_list.NEXT(row_index);
END LOOP;
RETURN total;
END compute_sum;`
FUNCTION compute_sum (the_list IN list_t) RETURN NUMBER
AS
row_index PLS_INTEGER := the_list.LAST;
total NUMBER := 0;
BEGIN
LOOP
EXIT WHEN row_index IS NULL;
total := total + the_list(row_index);
row_index := the_list.PRIOR(row_index);
END LOOP;
RETURN total;
END compute_sum;
Declaring an associative array collection type
TYPE table_type_name IS TABLE OF datatype [ NOT NULL ]
INDEX BY index_type;
The PRIOR and NEXT Methods
Use the PRIOR and NEXT methods with nested tables, associative arrays, and VARRAYs to
navigate through the contents of a collection.
PRIOR returns the next-lower index value in use relative to i; NEXT returns the next higher.
In the following example, this function returns the sum of elements in a list_t collection of
numbers:

FUNCTION compute_sum (the_list IN list_t) RETURN NUMBER


AS
row_index PLS_INTEGER := the_list.FIRST;
total NUMBER := 0;
BEGIN
LOOP
EXIT WHEN row_index IS NULL;
total := total + the_list(row_index);
row_index := the_list.NEXT(row_index);
END LOOP;
RETURN total;
END compute_sum;
Here is that same program working from the last to the very first defined row in the
collection:

FUNCTION compute_sum (the_list IN list_t) RETURN NUMBER


AS
row_index PLS_INTEGER := the_list.LAST;
total NUMBER := 0;
BEGIN
LOOP
EXIT WHEN row_index IS NULL;
total := total + the_list(row_index);
row_index := the_list.PRIOR(row_index);
END LOOP;
RETURN total;
END compute_sum;
Declaring an associative array collection type
The TYPE statement for an associative array has the following format:

TYPE table_type_name IS TABLE OF datatype [ NOT NULL ]


INDEX BY index_type;
Declaring a nested table or VARRAY
To create a nested table datatype that lives in the database

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 ];
To declare a nested table datatype in PL/SQL

TYPE type_name IS TABLE OF element_datatype [ NOT NULL ];


TYPE type_name IS VARRAY (max_elements)
OF element_datatype [ NOT NULL ];
Declaring and Initializing Collection Variables
collection_name collection_type [:= collection_type (...)];
When you are declaring a nested table or VARRAY, you must initialize the collection before
using it. Otherwise, you will receive ORA-06531: Reference to uninitialized collection error
Assigning rows from a relational table
DECLARE
TYPE emp_copy_t IS TABLE OF employees%ROWTYPE INDEX BY PLS_INTEGER;
l_emps emp_copy_t;
BEGIN
FOR emp_rec IN (SELECT * FROM employees)
LOOP
l_emps (emp_rec.employee_id) := emp_rec;
END LOOP;
END;
Multilevel Collections Example
CREATE TYPE vet_visit_t IS OBJECT (
visit_date DATE,
reason VARCHAR2 (100)
);
/
CREATE TYPE vet_visits_t IS TABLE OF vet_visit_t;
/
CREATE TYPE pet_t IS OBJECT (
tag_no INTEGER,
name VARCHAR2 (60),
petcare vet_visits_t,
MEMBER FUNCTION set_tag_no (new_tag_no IN INTEGER) RETURN pet_t);
/
DECLARE

TYPE bunch_of_pets_t
IS
TABLE OF pet_t INDEX BY PLS_INTEGER;

my_pets bunch_of_pets_t;

BEGIN

my_pets (1) :=
pet_t (
100
, 'Mercury'
, vet_visits_t (vet_visit_t ('01-Jan-2001', 'Clip wings')
, vet_visit_t ('01-Apr-2002', 'Check cholesterol')
)
);
DBMS_OUTPUT.put_line (my_pets (1).name);
DBMS_OUTPUT.put_line
(my_pets(1).petcare.LAST).reason);
DBMS_OUTPUT.put_line (my_pets.COUNT);
DBMS_OUTPUT.put_line (my_pets (1).petcare.LAST);
END;
MULTISET Example
CREATE TABLE birds (
genus VARCHAR2(128),
species VARCHAR2(128),
colors color_tab_t,
PRIMARY KEY (genus, species)
);

CREATE TABLE bird_habitats (


genus VARCHAR2(128),
species VARCHAR2(128),
country VARCHAR2(60),
FOREIGN KEY (genus, species) REFERENCES birds (genus, species)
);

CREATE TYPE country_tab_t AS TABLE OF VARCHAR2(60);able


DECLARE
CURSOR bird_curs IS
SELECT b.genus, b.species,
CAST(MULTISET(SELECT bh.country FROM bird_habitats bh
WHERE bh.genus = b.genus
AND bh.species = b.species)
AS country_tab_t)
FROM birds b;
bird_row bird_curs%ROWTYPE;
BEGIN
OPEN bird_curs;
FETCH bird_curs into bird_row;
CLOSE bird_curs;
END;
The Table pseudofunction
Prior to Oracle Database 12c, you could only use the TABLE pseudofunction with nested
tables and VARRAYs, and only if their types were defined at the schema level with
CREATE OR REPLACE TYPE (there was one exception to this rule: pipelined table
functions could work with types defined in package specifications). Starting with Oracle
Database 12c, however, you can also use the TABLE pseudofunction with nested tables,
VARRAYs, and integer-indexed associative arrays, as long as their types are defined in a
package specification.

CREATE OR REPLACE PACKAGE aa_pkg


IS
TYPE strings_t IS TABLE OF VARCHAR2 (100)
INDEX BY PLS_INTEGER;
END;
/
/* Populate the collection, then use a cursor FOR loop
to select all elements and display them. */

DECLARE
happyfamily aa_pkg.strings_t;
BEGIN
happyfamily (1) := 'Me';
happyfamily (2) := 'You';

FOR rec IN ( SELECT COLUMN_VALUE family_name


FROM TABLE (happyfamily)
ORDER BY family_name)
LOOP
DBMS_OUTPUT.put_line (rec.family_name);
END LOOP;
END;
/
You cannot use TABLE with a locally declared collection type, as you can see here.

/* File on web: 12c_table_pf_with_aa.sql */


DECLARE
TYPE strings_t IS TABLE OF VARCHAR2 (100)
INDEX BY PLS_INTEGER;

happyfamily strings_t;
BEGIN
happyfamily (1) := 'Me';
happyfamily (2) := 'You';

FOR rec IN ( SELECT COLUMN_VALUE family_name


FROM TABLE (happyfamily)
ORDER BY family_name)
LOOP
DBMS_OUTPUT.put_line (rec.family_name);
END LOOP;
END;
/

ERROR at line 12:


ORA-06550: line 12, column 32:
PLS-00382: expression is of wrong type
ORA-06550: line 12, column 25:
PL/SQL: ORA-22905: cannot access rows from a non-nested table item
Nested Table Multiset Operations
The essential advance made in collections starting with Oracle Database 10g is that the
database treats nested tables more like the multisets that they actually are. The database
provides high-level set operations that can be applied to nested tables (and, for the time
being, only to nested tables). This table offers a brief summary of these set-level capabilities.

Return
Operation Description
value

Compares two nested tables, and returns TRUE if


= BOOLEAN they have the same named type and cardinality and
if the elements are equal.

Compares two nested tables, and returns FALSE if


<> or != BOOLEAN they differ in named type, cardinality, or equality
of elements.

Returns TRUE [FALSE] if the nested table to the


[NOT] IN () BOOLEAN left of IN exists in the list of nested tables in the
parentheses.

Performs a MINUS set operation on nested


tables x and y, returning a nested table whose
x MULTISET
NESTED elements are in x, but not in y. x, y, and the returned
EXCEPT
TABLE nested table must all be of the same type. The
[DISTINCT] y
DISTINCT keyword instructs Oracle to eliminate
duplicates in the resulting nested table.

Performs an INTERSECT set operation on nested


tables x and y, returning a nested table whose
x MULTISET elements are in both x and y. x, y, and the returned
NESTED
INTERSECT nested table must all be of the same type. The
TABLE
[DISTINCT] y DISTINCT keyword forces the elimination of
duplicates from the returned nested table, including
duplicates of NULL, if they exist.

Performs a UNION set operation on nested


tables x and y, returning a nested table whose
elements include all those in x as well as those
x MULTISET
NESTED in y. x, y, and the returned nested table must all be
UNION
TABLE of the same type. The DISTINCT keyword forces
[DISTINCT] y
the elimination of duplicates from the returned
nested table, including duplicates of NULL, if they
exist.

SET(x) NESTED Returns nested table x without duplicate elements.


Return
Operation Description
value

TABLE

Returns TRUE [FALSE] if the nested table x is


x IS [NOT] A SET BOOLEAN
composed of unique elements.

x IS [NOT] Returns TRUE [FALSE] if the nested table x is


BOOLEAN
EMPTY empty.

Returns TRUE [FALSE] if the expression e is a


e [NOT] MEMBER member of the nested table x.Warning: MEMBER
BOOLEAN
[OF] x inside SQL statements is very inefficient, while in
PL/SQL performance is much better.

y [NOT]
SUBMULTISET
[OF] x

Why Use Cursor Variables?


You can do all of the following with cursor variables:
 Associate them with different queries at different times in your program execution. In
other words, a single cursor variable can be used to fetch from different result sets.
 Pass them as an argument to a procedure or function. You can, in essence, share the
results of a cursor by passing a reference to that result set.
 Employ the full functionality of static PL/SQL cursors. You can OPEN, CLOSE, and
FETCH with cursor variables within your PL/SQL programs. You can also reference
the standard cursor attributes—%ISOPEN, %FOUND, %NOTFOUND, and
%ROWCOUNT—for cursor variables.
 Assign the contents of one cursor (and its result set) to another cursor variable.
Because the cursor variable is a variable, it can be used in assignment operations.
There are restrictions on referencing this kind of variable, however, as I’ll discuss
later in this chapter
Declaring REF CURSOR Types
TYPE cursor_type_name IS REF CURSOR [ RETURN return_type ];
Starting with Oracle9i Database, Oracle provides a predefined weak REF CURSOR type
named SYS_REFCURSOR

DECLARE
my_cursor SYS_REFCURSOR;
Declaring Cursor Variables
cursor_name cursor_type_name;
Opening Cursor Variables
OPEN cursor_name FOR select_statement;
Fetching from Cursor Variables
FETCH cursor_variable_name INTO record_name;
FETCH cursor_variable_name INTO variable_name, variable_name ...;
Compile-time rowtype matching rules
These are the rules that PL/SQL follows at compile time:
 Two cursor variables (including procedure parameters) are compatible for assignments
and argument passing if any of the following are true:

o Both variables (or parameters) are of a strong REF CURSOR type with the
same rowtype_name.

o Both variables (or parameters) are of a weak REF CURSOR type, regardless of
the rowtype_name.

o One variable (or parameter) is of any strong REF CURSOR type, and the other is of
any weak REF CURSOR type.

 A cursor variable (or parameter) of a strong REF CURSOR type may be OPEN FOR a query
that returns a rowtype that is structurally equal to the rowtype_name in the original type
declaration.

 A cursor variable (or parameter) of a weak REF CURSOR type may be OPEN FOR any
query. The FETCH from such a variable is allowed INTO any list of variables or record structure.

You might also like