#=======================================================================
# Writer
: Lokanadham
Thandlam
#=======================================================================
/*A
composite data type stores values that have internal components.
You can pass
entire composite variables to subprograms as parameters, and you can access
internal components of composite variables individually.
Internal
components can be either scalar or composite. You can use scalar components
wherever you can use scalar variables.
PL/SQL lets
you define two kinds of composite data types, collection and record.
You can use
composite components wherever you can use composite variables of the same type.
In a
collection, the internal components always have the same data type, and are
called elements.
You can
access each element of a collection variable by its unique index, with this
syntax: variable_name(index). To create a collection variable,
you either
define a collection type and then create a variable of that type or use %TYPE.
In a record,
the internal components can have different data types, and are called fields.
You can
access each field of a record variable by its name, with this syntax:
variable_name.field_name. To create a record variable,
you either
define a RECORD type and then create a variable of that type or use %ROWTYPE or
%TYPE.
You can
create a collection of records, and a record that contains collections.
PL/SQL
Collection Type:
1.
Associative array (PL/SQL table or index-by table)
2. VARRAY (variable-size
array)
3. Nested
table*/
--Example
5–1 Associative ARRAY INDEXED BY STRING
DECLARE
-- Associative array indexed
by string:
TYPE population IS TABLE OF NUMBER --
Associative array type
INDEX BY VARCHAR2 (64); -- indexed by string
city_population population; --
Associative array variable
i VARCHAR2 (64); -- Scalar
variable
BEGIN
-- Add elements (key-value
pairs) to associative array:
city_population ('Smallville') := 2000;
city_population ('Midland') := 750000;
city_population ('Megalopolis') := 1000000;
-- Change value associated
with key 'Smallville':
city_population ('Smallville') := 2001;
-- Print associative array:
i :=
city_population.FIRST; -- Get first
element of array
WHILE i IS NOT NULL
LOOP
DBMS_OUTPUT.PUT_LINE (
'Population of ' || i || ' is ' ||
city_population (i));
i :=
city_population.NEXT (i); -- Get next
element of array
END LOOP;
END;
/
--Example
5–2 FUNCTION Returns Associative ARRAY INDEXED BY PLS_INTEGER
DECLARE
TYPE
sum_multiples IS TABLE OF PLS_INTEGER
INDEX BY PLS_INTEGER;
n PLS_INTEGER := 5; -- number of multiples to sum
for display
sn PLS_INTEGER := 10; -- number of
multiples to sum
m PLS_INTEGER := 3; -- multiple
FUNCTION
get_sum_multiples (multiple IN PLS_INTEGER, num IN PLS_INTEGER)
RETURN
sum_multiples
IS
s
sum_multiples;
BEGIN
FOR i IN 1 .. num
LOOP
s (i) := multiple * ( (i * (i + 1)) / 2); -- sum of multiples
END LOOP;
RETURN s;
END
get_sum_multiples;
BEGIN
DBMS_OUTPUT.PUT_LINE (
'Sum of the first '
|| TO_CHAR (n)
|| ' multiples of '
|| TO_CHAR (m)
|| ' is '
|| TO_CHAR (get_sum_multiples
(m, sn) (n)));
END;
/
--Example
5–3 Declaring Associative ARRAY CONSTANT
CREATE OR REPLACE PACKAGE My_Types
AUTHID CURRENT_USER
IS
TYPE My_AA IS TABLE OF VARCHAR2 (20)
INDEX BY PLS_INTEGER;
FUNCTION Init_My_AA
RETURN My_AA;
END My_Types;
/
CREATE OR REPLACE PACKAGE BODY My_Types
IS
FUNCTION Init_My_AA
RETURN My_AA
IS
Ret
My_AA;
BEGIN
Ret (-10) := '-ten';
Ret (0) := 'zero';
Ret (1) := 'one';
Ret (2) := 'two';
Ret (3) := 'three';
Ret (4) := 'four';
Ret
(9) := 'nine';
RETURN Ret;
END Init_My_AA;
END My_Types;
/
DECLARE
v CONSTANT My_Types.My_AA := My_Types.Init_My_AA ();
BEGIN
DECLARE
Idx
PLS_INTEGER := v.FIRST ();
BEGIN
WHILE Idx IS NOT NULL
LOOP
DBMS_OUTPUT.PUT_LINE (TO_CHAR (Idx, '999') || LPAD (v (Idx), 7));
Idx := v.NEXT (Idx);
END LOOP;
END;
END;
/
--Example
5–4 VARRAY (VARIABLE-SIZE ARRAY)
DECLARE
TYPE Foursome IS VARRAY (4) OF VARCHAR2 (15); -- VARRAY
type
-- varray variable
initialized with constructor:
team
Foursome
:= Foursome ('John',
'Mary',
'Alberto',
'Juanita');
PROCEDURE print_team (heading VARCHAR2)
IS
BEGIN
DBMS_OUTPUT.PUT_LINE (heading);
FOR i IN 1 .. 4
LOOP
DBMS_OUTPUT.PUT_LINE (i || '.' || team (i));
END LOOP;
DBMS_OUTPUT.PUT_LINE ('---');
END;
BEGIN
print_team ('2001 Team:');
team (3) := 'Pierre'; -- Change
values of two elements
team (4) := 'Yvonne';
print_team ('2005 Team:');
-- Invoke constructor to
assign new values to varray variable:
team :=
Foursome ('Arun',
'Amitha',
'Allan',
'Mae');
print_team ('2009 Team:');
END;
/
--Example
5–5 NESTED TABLE OF LOCAL TYPE
DECLARE
TYPE Roster IS TABLE OF VARCHAR2 (15); -- nested
table type
-- nested table variable
initialized with constructor:
names
Roster
:= Roster ('D Caruso',
'J Hamil',
'D Piro',
'R Singh');
PROCEDURE print_names
(heading VARCHAR2)
IS
BEGIN
DBMS_OUTPUT.PUT_LINE (heading);
FOR i IN names.FIRST .. names.LAST
LOOP -- For first
to last element
DBMS_OUTPUT.PUT_LINE (names (i));
END LOOP;
DBMS_OUTPUT.PUT_LINE ('---');
END;
BEGIN
print_names ('Initial
Values:');
names (3) := 'P Perez'; -- Change
value of one element
print_names ('Current
Values:');
names := Roster ('A Jansen', 'B Gupta'); -- Change entire table
print_names ('Current
Values:');
END;
/
--Example
5–6 NESTED TABLE OF STANDALONE TYPE
CREATE OR REPLACE TYPE nt_type IS TABLE OF NUMBER;
/
CREATE OR REPLACE PROCEDURE print_nt (nt nt_type)
AUTHID DEFINER
IS
i NUMBER;
BEGIN
i := nt.FIRST;
IF i IS NULL
THEN
DBMS_OUTPUT.PUT_LINE ('nt is empty');
ELSE
WHILE i IS NOT NULL
LOOP
DBMS_OUTPUT.PUT ('nt.(' || i || ') = ');
DBMS_OUTPUT.PUT_LINE (NVL (TO_CHAR (nt (i)), 'NULL'));
i := nt.NEXT (i);
END LOOP;
END IF;
DBMS_OUTPUT.PUT_LINE ('---');
END print_nt;
/
DECLARE
nt
nt_type := nt_type (); -- nested
table variable initialized to empty
BEGIN
print_nt (nt);
nt :=
nt_type (90,
9,
29,
58);
print_nt (nt);
END;
/
--Example
5–7 Initializing Collection (VARRAY) VARIABLE TO EMPTY
DECLARE
TYPE Foursome IS VARRAY (4) OF VARCHAR2 (15);
team
Foursome := Foursome (); --
initialize to empty
PROCEDURE print_team (heading VARCHAR2)
IS
BEGIN
DBMS_OUTPUT.PUT_LINE (heading);
IF team.COUNT = 0
THEN
DBMS_OUTPUT.PUT_LINE ('Empty');
ELSE
FOR i IN 1 .. 4
LOOP
DBMS_OUTPUT.PUT_LINE (i || '.' || team (i));
END LOOP;
END IF;
DBMS_OUTPUT.PUT_LINE ('---');
END;
BEGIN
print_team ('Team:');
team :=
Foursome ('John',
'Mary',
'Alberto',
'Juanita');
print_team ('Team:');
END;
/
--Example
5–8 DATA TYPE COMPATIBILITY FOR Collection Assignment
DECLARE
TYPE triplet IS VARRAY (3) OF VARCHAR2 (15);
TYPE trio IS VARRAY (3) OF VARCHAR2 (15);
group1
triplet := triplet ('Jones', 'Wong', 'Marceau');
group2
triplet;
group3
trio;
BEGIN
group2 := group1; -- succeeds
group3 := group1;
-- fails
END;
/
--Example
5–9 Assigning NULL VALUE TO NESTED TABLE VARIABLE
DECLARE
TYPE dnames_tab IS TABLE OF VARCHAR2 (30);
dept_names
dnames_tab
:= dnames_tab ('Shipping',
'Sales',
'Finance',
'Payroll'); --
Initialized to non-null value
empty_set
dnames_tab; -- Not
initialized, therefore null
PROCEDURE
print_dept_names_status
IS
BEGIN
IF dept_names IS NULL
THEN
DBMS_OUTPUT.PUT_LINE ('dept_names is
null.');
ELSE
DBMS_OUTPUT.PUT_LINE
('dept_names is not null.');
END IF;
END
print_dept_names_status;
BEGIN
print_dept_names_status;
dept_names := empty_set; -- Assign null collection to
dept_names.
print_dept_names_status;
dept_names :=
dnames_tab ('Shipping',
'Sales',
'Finance',
'Payroll'); --
Re-initialize dept_names
print_dept_names_status;
END;
/
--Example
5–10 Assigning SET Operation Results TO NESTED TABLE VARIABLE
DECLARE
TYPE nested_typ IS TABLE OF NUMBER;
nt1
nested_typ := nested_typ (1, 2, 3);
nt2
nested_typ := nested_typ (3, 2, 1);
nt3
nested_typ
:= nested_typ (2,
3,
1,
3);
nt4
nested_typ := nested_typ (1, 2, 4);
answer
nested_typ;
PROCEDURE
print_nested_table (nt nested_typ)
IS
output
VARCHAR2 (128);
BEGIN
IF nt IS NULL
THEN
DBMS_OUTPUT.PUT_LINE ('Result: null
set');
ELSIF nt.COUNT = 0
THEN
DBMS_OUTPUT.PUT_LINE ('Result: empty
set');
ELSE
FOR i IN nt.FIRST .. nt.LAST
LOOP -- For first
to last element
output := output ||
nt (i) || ' ';
END LOOP;
DBMS_OUTPUT.PUT_LINE ('Result: ' || output);
END IF;
END
print_nested_table;
BEGIN
answer := nt1 MULTISET UNION nt4;
print_nested_table (answer);
answer := nt1 MULTISET UNION nt3;
print_nested_table (answer);
answer := nt1 MULTISET UNION DISTINCT nt3;
print_nested_table (answer);
answer := nt2 MULTISET INTERSECT nt3;
print_nested_table (answer);
answer := nt2 MULTISET INTERSECT DISTINCT nt3;
print_nested_table (answer);
answer := SET (nt3);
print_nested_table (answer);
answer := nt3 MULTISET EXCEPT nt2;
print_nested_table (answer);
answer := nt3 MULTISET EXCEPT DISTINCT nt2;
print_nested_table (answer);
END;
/
--Example
5–11 Two-Dimensional VARRAY (VARRAY OF VARRAYS)
DECLARE
TYPE t1 IS VARRAY (10) OF INTEGER; -- varray of
integer
va
t1 := t1 (2, 3, 5);
TYPE nt1 IS VARRAY (10) OF t1; -- varray of
varray of integer
nva
nt1
:= nt1 (va,
t1 (55, 6, 73),
t1 (2, 4),
va);
i INTEGER;
va1
t1;
BEGIN
i := nva (2) (3);
DBMS_OUTPUT.PUT_LINE ('i = ' || i);
nva.EXTEND;
nva (5) := t1 (56, 32); -- replace
inner varray elements
nva (4) :=
t1 (45,
43,
67,
43345); -- replace
an inner integer element
nva (4) (4) := 1; -- replace 43345 with 1
nva (4).EXTEND; -- add
element to 4th varray element
nva (4) (5) := 89; -- store
integer 89 there
END;
/
--Example
5–12 NESTED TABLES OF NESTED TABLES AND VARRAYS OF Integers
DECLARE
TYPE tb1 IS TABLE OF VARCHAR2 (20); -- nested table of strings
vtb1
tb1 := tb1 ('one', 'three');
TYPE ntb1 IS TABLE OF tb1; -- nested table of nested
tables of strings
vntb1
ntb1 := ntb1 (vtb1);
TYPE tv1 IS VARRAY (10) OF INTEGER; -- varray of
integers
TYPE ntb2 IS TABLE OF tv1; -- nested table of varrays of
integers
vntb2
ntb2 := ntb2 (tv1 (3, 5), tv1 (5, 7, 3));
BEGIN
vntb1.EXTEND;
vntb1 (2) := vntb1 (1);
vntb1.DELETE (1); -- delete
first element of vntb1
vntb1 (2).DELETE (1); -- delete
first string from second table in nested table
END;
/
--Example
5–13 NESTED TABLES OF Associative Arrays AND VARRAYS OF Strings
DECLARE
TYPE tb1 IS TABLE OF INTEGER
INDEX BY PLS_INTEGER; --
associative arrays
v4
tb1;
v5
tb1;
TYPE aa1 IS TABLE OF tb1
INDEX BY PLS_INTEGER; --
associative array of
v2
aa1; --
associative arrays
TYPE va1 IS VARRAY (10) OF VARCHAR2 (20); -- varray of strings
v1
va1 := va1 ('hello', 'world');
TYPE ntb2 IS TABLE OF va1
INDEX BY PLS_INTEGER; -- associative array of
varrays
v3
ntb2;
BEGIN
v4 (1) := 34; -- populate
associative array
v4 (2) := 46456;
v4 (456) := 343;
v2 (23) := v4; -- populate associative array
of associative arrays
v3 (34) :=
va1 (33,
456,
656,
343); -- populate
associative array varrays
v2 (35) := v5; -- assign
empty associative array to v2(35)
v2 (35) (2) := 78;
END;
/
--Example
5–14 Comparing VARRAY AND NESTED TABLE Variables TO NULL
DECLARE
TYPE Foursome IS VARRAY (4) OF VARCHAR2 (15); -- VARRAY
type
team
Foursome;
-- varray variable
TYPE Roster IS TABLE OF VARCHAR2 (15); -- nested
table type
names
Roster := Roster ('Adams', 'Patel'); -- nested
table variable
BEGIN
IF team IS NULL
THEN
DBMS_OUTPUT.PUT_LINE ('team IS NULL');
ELSE
DBMS_OUTPUT.PUT_LINE ('team IS NOT
NULL');
END IF;
IF names IS NOT NULL
THEN
DBMS_OUTPUT.PUT_LINE ('names IS NOT
NULL');
ELSE
DBMS_OUTPUT.PUT_LINE ('names IS
NULL');
END IF;
END;
/
--Example
5–15 Comparing NESTED TABLES FOR Equality AND Inequality
DECLARE
TYPE dnames_tab IS TABLE OF VARCHAR2 (30); -- element
type is not record type
dept_names1
dnames_tab
:= dnames_tab ('Shipping',
'Sales',
'Finance',
'Payroll');
dept_names2
dnames_tab
:= dnames_tab ('Sales',
'Finance',
'Shipping',
'Payroll');
dept_names3
dnames_tab := dnames_tab ('Sales', 'Finance', 'Payroll');
BEGIN
IF dept_names1
= dept_names2
THEN
DBMS_OUTPUT.PUT_LINE ('dept_names1 =
dept_names2');
END IF;
IF dept_names2
!= dept_names3
THEN
DBMS_OUTPUT.PUT_LINE ('dept_names2
!= dept_names3');
END IF;
END;
/
--Example
5–16 Comparing NESTED TABLES WITH SQL MULTISET Conditions
DECLARE
TYPE nested_typ IS TABLE OF NUMBER;
nt1
nested_typ := nested_typ (1, 2, 3);
nt2
nested_typ := nested_typ (3, 2, 1);
nt3
nested_typ
:= nested_typ (2,
3,
1,
3);
nt4
nested_typ := nested_typ (1, 2, 4);
PROCEDURE testify (truth BOOLEAN := NULL, quantity NUMBER := NULL)
IS
BEGIN
IF truth IS NOT NULL
THEN
DBMS_OUTPUT.PUT_LINE (
CASE truth WHEN TRUE THEN 'True' WHEN FALSE THEN 'False' END);
END IF;
IF quantity IS NOT NULL
THEN
DBMS_OUTPUT.PUT_LINE (quantity);
END IF;
END;
BEGIN
testify (truth => (nt1 IN (nt2, nt3, nt4))); -- condition
testify (truth => (nt1 SUBMULTISET OF nt3)); -- condition
testify (truth => (nt1 NOT SUBMULTISET OF nt4)); -- condition
testify (truth => (4 MEMBER OF nt1)); -- condition
testify (truth => (nt3 IS A SET)); -- condition
testify (truth => (nt3 IS NOT A SET)); -- condition
testify (truth => (nt1 IS EMPTY)); -- condition
testify (quantity => (CARDINALITY (nt3))); -- function
testify (quantity => (CARDINALITY (SET (nt3)))); -- 2 functions
END;
/
--TABLE 5–2
Collection Methods
--Method
TYPE Description
DELETE
Procedure
Deletes elements from collection.
TRIM
Procedure
Deletes elements from end of varray or nested table.
EXTEND
Procedure
Adds elements to end of varray or nested table.
EXISTS
Function
Returns TRUE if and only if specified element of varray or nested table exists.
FIRST
Function
Returns first index in collection.
LAST
Function
Returns last index in collection.
COUNT
Function
Returns number of elements in collection.
LIMIT
Function
Returns maximum number of elements
that collection can have.
PRIOR
Function
Returns index that
precedes specified index.
NEXT
Function
Returns index that
succeeds specified index.
--Example
5–17 DELETE Method with Nested Table
DECLARE
nt nt_type := nt_type(11, 22, 33, 44, 55, 66);
BEGIN
print_nt (nt);
nt.DELETE (2); -- Delete
second element
print_nt (nt);
nt (2) := 2222; -- Restore
second element
print_nt (nt);
nt.DELETE (2, 4); -- Delete
range of elements
print_nt (nt);
nt (3) := 3333; -- Restore
third element
print_nt (nt);
nt.DELETE; -- Delete
all elements
print_nt (nt);
END;
/
--Example
5–18 DELETE Method WITH Associative ARRAY INDEXED BY STRING
DECLARE
TYPE aa_type_str
IS TABLE OF INTEGER
INDEX BY VARCHAR2 (10);
aa_str
aa_type_str;
PROCEDURE
print_aa_str
IS
i VARCHAR2 (10);
BEGIN
i := aa_str.FIRST;
IF i IS NULL
THEN
DBMS_OUTPUT.PUT_LINE ('aa_str is
empty');
ELSE
WHILE i IS NOT NULL
LOOP
DBMS_OUTPUT.PUT ('aa_str.(' || i || ') = ');
DBMS_OUTPUT.PUT_LINE (NVL (TO_CHAR ( (aa_str (i)), 'NULL')) ;i := aa_str.NEXT(i);
END LOOP;
END IF;
DBMS_OUTPUT.PUT_LINE('---');
END
print_aa_str;
BEGIN
aa_str('M') := 13;
aa_str('Z') := 26;
aa_str('C') := 3;
print_aa_str;
aa_str.DELETE; -- Delete all
elements
print_aa_str;
aa_str('M') := 13; -- Replace
deleted element with same value
aa_str('Z') := 260; -- Replace
deleted element with new value
aa_str('C') := 30; -- Replace
deleted element with new value
aa_str('W') := 23; -- Add new
element
aa_str('J') := 10; -- Add new
element
aa_str('N') := 14; -- Add new
element
aa_str('P') := 16; -- Add new
element
aa_str('W') := 23; -- Add new
element
aa_str('J') := 10; -- Add new
element
print_aa_str;
aa_str.DELETE('C'); -- Delete
one element
print_aa_str;
aa_str.DELETE('N','W'); -- Delete
range of elements
print_aa_str;
aa_str.DELETE('Z','M'); -- Does
nothing
print_aa_str;
END;
/
--Example
5–19 TRIM Method WITH NESTED TABLE
DECLARE
nt
nt_type
:= nt_type (11,
22,
33,
44,
55,
66);
BEGIN
print_nt (nt);
nt.TRIM; -- Trim last
element
print_nt (nt);
nt.DELETE (4); -- Delete
fourth element
print_nt (nt);
nt.TRIM (2); -- Trim last
two elements
print_nt (nt);
END;
/
--Example
5–20 EXTEND Method WITH NESTED TABLE
DECLARE
nt
nt_type := nt_type (11, 22, 33);
BEGIN
print_nt (nt);
nt.EXTEND (2, 1); -- Append
two copies of first element
print_nt (nt);
nt.DELETE (5); -- Delete
fifth element
print_nt (nt);
nt.EXTEND; -- Append
one null element
print_nt (nt);
END;
/
--Example
5–21 EXISTS Method WITH NESTED TABLE
DECLARE
TYPE NumList IS TABLE OF INTEGER;
n
NumList
:= NumList (1,
3,
5,
7);
BEGIN
n.DELETE (2); -- Delete
second element
FOR i IN 1 .. 6
LOOP
IF n.EXISTS (i)
THEN
DBMS_OUTPUT.PUT_LINE ('n(' || i || ') = ' || n (i));
ELSE
DBMS_OUTPUT.PUT_LINE
('n(' || i || ') does not exist');
END IF;
END LOOP;
END;
/
--Example
5–22 FIRST AND LAST VALUES FOR Associative ARRAY INDEXED BY PLS_INTEGER
DECLARE
TYPE aa_type_int
IS TABLE OF INTEGER
INDEX BY PLS_INTEGER;
aa_int
aa_type_int;
PROCEDURE
print_first_and_last
IS
BEGIN
DBMS_OUTPUT.PUT_LINE ('FIRST = ' || aa_int.FIRST);
DBMS_OUTPUT.PUT_LINE ('LAST = ' || aa_int.LAST);
END
print_first_and_last;
BEGIN
aa_int (1) := 3;
aa_int (2) := 6;
aa_int (3) := 9;
aa_int (4) := 12;
DBMS_OUTPUT.PUT_LINE ('Before
deletions:');
print_first_and_last;
aa_int.DELETE (1);
aa_int.DELETE (4);
DBMS_OUTPUT.PUT_LINE ('After
deletions:');
print_first_and_last;
END;
/
--Example
5–23 FIRST AND LAST VALUES FOR Associative ARRAY INDEXED BY STRING
DECLARE
TYPE aa_type_str
IS TABLE OF INTEGER
INDEX BY VARCHAR2 (10);
aa_str
aa_type_str;
PROCEDURE
print_first_and_last
IS
BEGIN
DBMS_OUTPUT.PUT_LINE ('FIRST = ' || aa_str.FIRST);
DBMS_OUTPUT.PUT_LINE ('LAST = ' || aa_str.LAST);
END
print_first_and_last;
BEGIN
aa_str ('Z') := 26;
aa_str ('A') := 1;
aa_str ('K') := 11;
aa_str ('R') := 18;
DBMS_OUTPUT.PUT_LINE ('Before
deletions:');
print_first_and_last;
aa_str.DELETE ('A');
aa_str.DELETE ('Z');
DBMS_OUTPUT.PUT_LINE ('After
deletions:');
print_first_and_last;
END;
/
--Example
5–24 Printing VARRAY WITH FIRST AND LAST IN FOR LOOP
DECLARE
TYPE team_type IS VARRAY (4) OF VARCHAR2 (15);
team
team_type;
PROCEDURE print_team (heading VARCHAR2)
IS
BEGIN
DBMS_OUTPUT.PUT_LINE (heading);
IF team IS NULL
THEN
DBMS_OUTPUT.PUT_LINE ('Does not
exist');
ELSIF team.FIRST IS NULL
THEN
DBMS_OUTPUT.PUT_LINE ('Has no
members');
ELSE
FOR i IN team.FIRST .. team.LAST
LOOP
DBMS_OUTPUT.PUT_LINE (i || '. ' || team (i));
END LOOP;
END IF;
DBMS_OUTPUT.PUT_LINE
('---');
END;
BEGIN
print_team ('Team Status:');
team := team_type (); -- Team is
funded, but nobody is on it.
print_team ('Team Status:');
team := team_type ('John', 'Mary'); -- Put 2
members on team.
print_team ('Initial
Team:');
team :=
team_type ('Arun',
'Amitha',
'Allan',
'Mae'); -- Change
team.
print_team ('New Team:');
END;
/