Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	
All About
PL/SQL Collections	
1	
Steven	Feuerstein	
Oracle	Developer	Advocate	for	PL/SQL	
Oracle	CorporaDon	
	
Email:	steven.feuerstein@oracle.com	
TwiLer:	@sfonplsql	
Blog:	stevenfeuersteinonplsql.blogspot.com	
YouTube:	PracDcally	Perfect	PL/SQL
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	2	
Resources	for	Oracle	Database	Developers	
•  Official	home	of	PL/SQL	-	oracle.com/plsql	
•  SQL-PL/SQL	discussion	forum	on	OTN	
hLps://community.oracle.com/community/database/developer-tools/sql_and_pl_sql	
•  PL/SQL	and	EBR	blog	by	Bryn	Llewellyn	-	hLps://blogs.oracle.com/plsql-and-ebr	
•  Oracle	Learning	Library	-	oracle.com/oll		
•  Weekly	PL/SQL	and	SQL	quizzes,	and	more	-	plsqlchallenge.oracle.com	
•  Ask	Tom	-	asktom.oracle.com	–	'nuff	said	
•  LiveSQL	-	livesql.oracle.com	–	script	repository	and	12/7	12c	database	
•  oracle-developer.net	-	great	content	from	Adrian	Billington	
•  oracle-base.com	-	great	content	from	Tim	Hall
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	3	
Agenda	
•  IntroducDon	and	overview		
•  Defining	and	using	collecDon	types	
•  Using	collecDon	methods	
•  Working	with	associaDve	arrays	
•  Working	with	nested	tables	
•  Working	with	varrays	
•  Using	collecDons	inside	SQL	
•  Benefits	of	Non-SequenDal	Indexing	
•  Using	String	Indexes	with	AssociaDve	Arrays	
•  Working	with	Nested	CollecDons	
•  Using	MULTISET	Operators	with	Nested	Tables	
•  Best	PracDces	for	CollecDons
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	4	
PL/SQL	Collec>ons	
•  A	collecDon	is	an	"ordered	group	of	elements,	
all	of	the	same	type."	(PL/SQL	User	Guide)	
– In	short,	a	"homogeneous"	list	of	"stuff"	
•  CollecDons	are	similar	to	single-dimensional	
arrays	in	other	programming	languages.	
– With	lots	of	subtle	differences,	as	well.	
•  CollecDons	almost	always	consume	Process	
Global	Area	memory.	
•  CollecDons	should	be	a	"go	to"	datatype	for	
Oracle	Database	developers	
1	 Apple	
22	 Pear	
100	 Orange	
10023	 Apricot
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	5	
System Global Area (SGA) of RDBMS Instance
PL/SQL	in	Shared	Memory	
Shared	Pool	
Large	Pool	
Reserved	Pool	
show_emps	calc_totals	 upd_salaries	
Select *
from emp
Shared	SQL	
Pre-parsed	
Update emp
Set sal=...
Library	cache	
Session	1	memory		(PGA/UGA)	
emp_rec emp%rowtype;
tot_tab tottabtype;
Session	2	memory		(PGA/UGA)	
emp_rec emp%rowtype;
tot_tab tottabtype;Session	1	 Session	2
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	6	
How	PL/SQL	uses	the	SGA,	PGA	and	UGA	
• The	SGA	contains	informaDon	that	can	be	shared	across	
sessions	connected	to	the	instance.	
– In	PL/SQL,	this	is	limited	to	package	staDc	constants.	
• The	User	Global	Area	contains	session-specific	data	that	
persists	across	server	call	boundaries	
– Package-level	data	
• The	Process	Global	Area	contains	session-specific	data	that	is	
released	when	the	current	server	call	terminates:	"local"	data.	
PACKAGE Pkg is
  Nonstatic_Constant CONSTANT PLS_INTEGER := My_Sequence.Nextval;
  Static_Constant    CONSTANT PLS_INTEGER := 42;
END Pkg;
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	7	
Calcula>ng	PGA	and	UGA	Consump>on	
• Oracle	keeps	track	of	and	shows	the	PGA	and	UGA	consumpDon	for	a	
session	in	the	v_$sesstat	dynamic	view.	
• With	the	correct	privileges,	PL/SQL	developers	can	analysis	their	code's	
memory	usage.	
show_pga_uga.sql
grantv$.sql
plsql_memory.pkg
plsql_memory_demo.sql
SELECT n.name, s.VALUE
FROM sys.v_$sesstat s, sys.v_$statname n
WHERE s.statistic# = n.statistic# AND s.sid = my_session.sid
AND n.name IN ('session uga memory', 'session pga memory')
BEGIN
plsql_memory.start_analysis;
run_my_application;
plsql_memory.show_memory_usage;
END;
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	8	
Why	use	collec>ons?	
• Generally,	to	manipulate	lists	of	informaDon	in	memory.		
– Of	course,	you	can	use	relaDonal	tables,	too.	
– CollecDon	manipulaDon	is	generally	much	faster	than	using	SQL	to	modify	the	
contents	of	tables.		
• CollecDons	enable	other	key	features	of	PL/SQL	
– BULK	COLLECT	and	FORALL	use	them	to	boost	mulD-row	SQL	performance	
– Serve	up	complex	datasets	of	informaDon	to	non-PL/SQL	host	environments	using	
table	func/ons.
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	9	
Different	Types	of	Collec>ons	
• Three	types	of	collecDons	
– AssociaDve	array	
– Nested	table	
– Varray	(varying	arrays)	
• AssociaDve	array	is	a	PL/SQL-only	datatype.	
• Nested	tables	and	varrays	can	be	used	within	PL/SQL	blocks	and	also	from	
within	SQL.	
– Column	tables	
– Table	funcDons
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	10	
Glossary	of	Terms	
•  Element
– A collection is made up of one or more elements, all of the same type. Also referred to
as "row."
– Can be of almost any valid PL/SQL type.
•  Index value
– The "location" in the collection in which an element is found. Also referred to as "row
number."
– Usually an integer, can also be a string (associative arrays only)
•  Dense
– Every index value between lowest and highest has a defined element.
•  Sparse
– One or more elements between lowest and highest index values may be undefined
(gaps).
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	11	
Defining	collec>on	types	
• Before	you	can	manipulate	a	collecDon	variable,	you	need	a	collecDon	
type	on	which	to	declare	the	variable.	
• Oracle	pre-defines	several	collecDon	types	in	various	supplied	packages.	
• DBMS_SQL	
– Dynamic	SQL-specific	types	
– Generic	types	(list	of	strings,	numbers,	etc.).	
• DBMS_OUTPUT	
– List	of	strings
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	12	
Defining	collec>on	types	
• Declare	all	collecDon	type	with	a	TYPE	statement.	
• AssociaDve	arrays	use	IS	TABLE	OF	and	the	INDEX	BY	clause.	
• Nested	tables	use	IS	TABLE	OF,	without	any	indexing	clause.	
• Varrays	use	IS	VARRAY	OF	syntax.	
TYPE coll_name IS TABLE OF element_type INDEX BY index_type;
TYPE coll_name IS TABLE OF element_type;
TYPE coll_name IS VARRAY (limit) OF element_type;
Associa>on	array	type	
Nested	table	type	
Varray	type
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	13	
Scope	for	Collec>on	Types	
• You	can	define	collecDon	types	in:	
– Local	block	–	can	be	used	only	in	that	block	
– Package		-	available	for	use	by	any	session	with	execute	authority	on	that	package	
– Schema	–	in	the	SQL	layer,	possible	only	for	nested	tables	and	varrays	
• Avoid	"reinvenDng"	collecDon	types	in	many	places	in	your	code.	
– They	are	excellent	candidates	for	shared	code	elements.
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	14	
Local	collec>on	types	
• TYPE	statement	found	in	declaraDon	secDon	of	block	(anonymous,	nested,	
subprogram)	
• It	is	defined	and	then	destroyed	each	Dme	the	block	is	executed.	
• You	should	avoid	local	types;	it	will	likely	lead	to	redundancies	in	your	
code.	
DECLARE
TYPE strings_t IS TABLE OF VARCHAR2(100);
PROCEDURE my_procedure
IS
TYPE strings_t IS TABLE OF VARCHAR2(100);
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	15	
Package-level	Types	
• When	defined	in	the	package	specificaDon,	it	becomes	a	"globally"	
available	type.	
– Any	session	with	EXECUTE	authority	on	the	package	can	use	the	type	to	declare	
collecDons.	
• In	the	package	body,	can	only	be	used	by	subprograms	of	that	package.	
• Excellent	repository	for	applicaDon-specific	types	
PACKAGE my_types
IS
TYPE strings_t IS TABLE OF VARCHAR2(100);
colltypes.pks
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	16	
Schema-level	Types	
• Defined	in	the	schema,	independent	of	any	PL/SQL	program	unit.	
• Any	session	with	EXECUTE	authority	on	type	can	use	it	to	declare	
collecDon	variables.	
– CollecDons	of	this	type	can	also	be	directly	referenced	inside	SQL	statements.	
• Can	only	be	used	with	nested	tables	and	varrays.	
– AssociaDve	arrays	are	PL/SQL-specific,	cannot	be	defined	at	the	schema	level	(SQL	
layer).	
CREATE OR REPLACE TYPE strings_t IS TABLE OF VARCHAR2(100)
GRANT EXECUTE ON strings_t TO PUBLIC
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	17	
Declaring	collec>on	variables	
• Once	you	have	defined	your	collecDon	type,	you	can	define	a	variable	
based	on	that.	
– The	same	as	for	any	type	of	data	in	PL/SQL	
CREATE TYPE hire_dates_t IS TABLE OF DATE;
CREATE PACKAGE my_types IS
TYPE strings_t IS TABLE OF VARCHAR2(100);
END my_types;
DECLARE
l_names my_types.string_t;
l_dates hire_dates_t;
l_dates HR.hire_dates_t;
l_strings DBMS_SQL.varchar2_table;
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	18	
Collec>on	Methods	
• The	term	method	is	used	to	describe	procedures	and	funcDons	that	
defined	in	a	class	or	object	type.	
– You	invoke	a	method	by	aLaching	it,	using	dot	notaDon,	to	the	name	of	the	type/
class	or	to	an	instance	of	the	class.	
• CollecDon	methods	are	procedures	and	funcDons	that	are	a6ached	to	a	
collecDon	variable.	
– First	introducDon	of	object-oriented	syntax	in	PL/SQL	–	way	back	in	Oracle	7.3.4!	
	
method_vs_proc.sql
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	19	
Methods	that	retrieve	informa>on	
• COUNT:	number	of	elements	currently	defined	in	collecDon.	
• EXISTS:	TRUE	if	the	specified	index	values	is	defined.	
• FIRST/LAST:	lowest/highest	index	values	of	defined	rows.	
• NEXT/PRIOR:	defined	index	value	ater/before	the	specified	index	value	.	
• LIMIT:	max.	number	of	elements	allowed	in	a	VARRAY.
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	20	
The	COUNT	Method	
• Returns	0	if	the	collecDon	is	empty.	
• Otherwise	returns	the	number	of	defined	index	values.	
• You	cannot	ask	for	a	count	of	elements	between	a	range	of	index	values.	
Too	bad...	
BEGIN
IF my_collection.COUNT > 0
THEN
/* We have some data in the collection */
...
END IF;
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	21	
Checking	for	element	existence	
• If	you	try	to	read	an	element	at	an	undefined	index	value,	Oracle	raises	
the	NO_DATA_FOUND	excepDon.	
– A	poor	decision	on	Oracle's	part.	
• Use	the	EXISTS	method	to	check	to	see	if	the	index	value	is	defined.	
BEGIN
IF my_collection.EXISTS (l_index)
THEN
DBMS_OUTPUT.PUT_LINE (my_collection (l_index));
END IF;
collection_exists.sql
plsqlloops.sp
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	22	
Naviga>ng	Through	Collec>ons	
• One	of	the	most	common	acDons	on	collecDons	is	looping	through	the	
contents.	
• You	can	use	WHILE,	simple	and	FOR	loops	to	perform	this	navigaDon.	
• The	characterisDcs	of	your	collecDon	will	determine	which	sort	of	loop	to	
use.
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	23	
Choose	the	Right	Loop	
• FOR	loop	
– Only	use	this	loop	when	you	want	to	iterate	through	every	element	between	the	low	
and	high	index	values.	
– Do	not	use	with	sparse	collecDons.	
• WHILE	and	simple	loops	
– Best	fit	for	sparse	collecDons	and	when	you	want	to	condi/onally	exit	from	your	loop.
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	24	
Naviga>on	methods	and	FOR	loops	
• With	FOR	loops,	you	will	use:	
– COUNT	–	when	the	first	index	value	in	the	collecDon	is	1.	
– FIRST	and	LAST	when	FIRST	may	not	be	one.	
BEGIN
FOR indx IN 1 .. my_collection.COUNT
LOOP
do_something_with (my_collection (indx));
END LOOP;
END;
BEGIN
FOR indx IN my_collection.FIRST .. my_collection.LAST
LOOP
do_something_with (my_collection (indx));
END LOOP;
END;
plsqlloops.sp
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	25	
Naviga>on	methods	&	WHILE/simple	loops	
• With	WHILE	and	simple	loops,	you	will	use	
– FIRST	and	NEXT	to	move	from	first	to	last	
– LAST	and	PRIOR	to	move	from	last	to	first	
rowind PLS_INTEGER := my_collection.FIRST;
BEGIN
LOOP
EXIT WHEN rowind IS NULL;
rowind := my_collection.NEXT (rowind);
END LOOP;
END;
rowind PLS_INTEGER := my_collection.LAST;
BEGIN
LOOP
EXIT WHEN rowind IS NULL;
rowind := my_collection.PRIOR (rowind);
END LOOP;
END;
plsqlloops.sp
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	26	
The	LIMIT	Method	
• Only	the	varray	has	a	pre-defined	upper	limit	on	the	number	of	elements	
that	can	defined	in	it.	
– Well,	the	other	collecDons	types	theore/cally	have	an	upper	limit,	but	you'll	never	
reach	it.	
• Use	the	LIMIT	method	to	determine	what	that	limit	is.	
DECLARE
TYPE max_of_five_t IS VARRAY (5) OF NUMBER;
l_list max_of_five_t := max_of_five_t();
BEGIN
DBMS_OUTPUT.put_line (l_list.LIMIT);
END;
varray_limit.sql
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	27	
Methods	that	change	a	collec>on	
• DELETE	deletes	one	or	more	rows	from	an	associaDve	array	or	nested	
table.	
• EXTEND	adds	rows	to	the	end	of	a	nested	table	or	varray.	
• TRIM	removes	rows	from	a	varray	or	nested	table.
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	28	
The	DELETE	Method	
• You	can	delete	one	or	more	rows	from	an	associaDve	array	or	
nested	table	using	DELETE.	
• Try	to	DELETE	from	a	varray	and	you	will	see	"PLS-00306:	wrong	
number	or	types	of	arguments	in	call	to	'DELETE'"	
• Low	and	high	index	values	do	not	have	to	exist.	
BEGIN
-- Delete all rows
myCollection.DELETE;
-- Delete one (the last) row
myCollection.DELETE (myCollection.LAST);
-- Delete a range of rows
myCollection.DELETE (1400, 17255);
END;
delete.sql
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	29	
Make	room	for	new	elements	w/	EXTEND	
• Use	EXTEND	only	with	varrays	and	nested	tables.	
• Tell	Oracle	to	add	N	number	of	new	elements	to	the	end	of	the	collecDon.	
• "Bulk"	extends	faster	than	individual	extends.	
– If	you	know	you	will	need	10,000	elements,	do	the	extend	in	a	single	step.	
• OpDonal:	specify	the	value	of	all	new	elements	from	an	exisDng	element.	
– Default	value	is	NULL.	
extend.sql
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	30	
Trimming	elements	from	end	of	collec>on	
• Use	TRIM	only	with	varrays	and	nested	tables.	
– Not	to	be	confused	with	the	TRIM	funcDon!	
• You	can	trim	one	or	mulDple	elements.	
– Default	is	1.	
– "ORA-06533:	Subscript	beyond	count"	error	if	you	to	trim	more	than	is	in	the	
collecDon.	
• TRIM	is	the	only	way	to	remove	elements	from	a	varray.	
– DELETE	is	not	supported.	
trim.sql
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	31	
Conclusions	–	Collec>on	Methods		
• Methods	make	it	much	easier	to	work	with	collecDons.	
• You	can	get	informaDon	about	the	collecDons	and	also	change	their	
contents.	
• When	you	use	the	navigaDon	methods,	make	sure	you	choose	the	
appropriate	type	of	loop	to	iterate	through	the	elements.
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	32	
Associa>ve	Arrays	
•  A	variable	declared	from	an	associaDve	array	type.	
•  An	unbounded	set	of	key-value	pairs.		
•  Each	key	is	unique,	and	serves	as	the	subscript	of	the	
element	that	holds	the	corresponding	value.		
•  You	can	access	elements	without	knowing	their	posiDons	
in	the	array,	and	without	traversing	the	array.	
1	 Apple	
22	 Pear	
100	 Orange	
10023	 Apricot	
DECLARE
TYPE list_of_names_t IS TABLE OF employees.last_name%TYPE
INDEX BY PLS_INTEGER;
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	33	
Associa>ve	Array	Background	
• It	was	the	first	type	of	collecDon	available	in	PL/SQL.	
• First	introduced	in	Oracle7	as	a	"PL/SQL	table"	(hence,	the	TABLE	OF	
syntax).	
• Renamed	in	Oracle8	to	"index-by	table"	when	nested	tables	and	varrays	
were	added.	
• In	9.2,	renamed	to	"associaDve	array"	with	the	advent	of	string	indexing.	
• Can	only	be	used	in	a	PL/SQL	context.
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	34	
Characteris>cs	of	Associa>ve	Arrays	
• TABLE	OF	datatypes	can	be	almost	any	valid	PL/SQL	type	(details	
to	follow).		
• INDEX	BY	type	can	be	integer	or	string.	
– This	means	you	can	essenDally	index	by	anything!	
– But	index	values	can	never	be	NULL.	
• AssociaDve	arrays	can	be	sparse.	
– Can	populate	elements	in	non-consecu/ve	index	values.	
– Easily	used	to	emulate	primary	keys	and	unique	indexes.	
DECLARE
TYPE list_of_names_t IS TABLE OF employees.last_name%TYPE
INDEX BY PLS_INTEGER;
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	35	
Simple	associa>ve	array	example	
DECLARE
TYPE list_of_names_t IS TABLE OF VARCHAR2 (20)
INDEX BY PLS_INTEGER;
happyfamily list_of_names_t;
l_index_value PLS_INTEGER := 88;
BEGIN
happyfamily (1) := 'Eli';
happyfamily (-15070) := 'Steven';
happyfamily (3) := 'Chris';
happyfamily (l_index_value) := 'Veva';
l_index_value := happyfamily.FIRST;
WHILE (l_index_value IS NOT NULL)
LOOP
DBMS_OUTPUT.put_line ( 'Value at index '
|| l_index_value
|| ' = '
|| happyfamily (l_index_value)
);
l_index_value := happyfamily.NEXT (l_index_value);
END LOOP;
END;
assoc_array_example.sql
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	36	
Associa>ve	array	of	records	example	
• It	is	very	easy	to	"emulate"	a	relaDonal	table	inside	one's	PL/SQL	code.	
– Or	use	any	other	kind	of	record	type.	
DECLARE
TYPE employees_aat IS TABLE OF employees%ROWTYPE
INDEX BY PLS_INTEGER;
l_employees employees_aat;
BEGIN
FOR employee_rec IN (SELECT * FROM employees)
LOOP
l_employees (l_employees.COUNT + 1) := employee_rec;
END LOOP;
END;
collection_of_records.sql
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	37	
Valid	TABLE	OF	datatypes	
• You	can	create	an	associaDve	array	of	almost	any	PL/SQL	or	SQL	datatype.	
– All	scalar	types,	including	Boolean	
– CollecDon	of	object	types	
– CollecDon	of	other	collecDons	
• There	are	some	restricDons:	
– Cannot	have	a	TABLE	OF	cursor	variables	or	excepDons.	
aa_table_of_invalid_types.sql
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	38	
Valid	Index	Values	
• No	prac/cal	limit	to	number	of	elements	you	can	
define	in	an	associaDve	array.	
• Integer	index	values	range	from		
-2,147,483,647	to	2,147,483,647	
– This	is	the	BINARY_INTEGER	range.		
– That's	almost	4.3	billion	elements!	
• String	index	values	can	have	any	value;	you	are	only	restricted	by	
maximum	number	of	elements	allowed	in	a	collecDon.	
– Which	you	will	never	reach.	
aa_limits.sql
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	39	
More	details	on	valid	INDEX	BY	types	
• The	INDEX	BY	clause	defines	the	indexing	for	the	collecDon.	
• You	can	define	the	index	datatype	of	your	associaDve	
array	type	to	be:	
– BINARY_INTEGER	and	any	sub-type	derived	from	
BINARY_INTEGER	
– VARCHAR2(n),	where	n	is	between	1	and	32767	
– %TYPE	against	a	database	column	that	is	consistent	with	the	
above	rules	
– A	SUBTYPE	against	any	of	the	above.	
indexby_options.sql
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	40	
Nested	Tables	and	Varrays	
• Added	in	Oracle8	as	part	of	the	object	model.	
• Types	can	be	defined	in	PL/SQL	or	a	schema-level	type.	
• You	must	ini/alize	before	using	the	collecDon.	
– There	are	some	excepDons,	as	with	BULK	COLLECT.	
• You	must	extend	to	make	room	for	new	elements.	
– There	are	some	excepDons,	as	with	BULK	COLLECT.	
• Columns	in	relaDonal	tables	can	be	of	type	nested	table	or	varray.	
– Oh,	but	the	denormalizaDon!
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	41	
Nested	Tables	
•  A	nested	table	is	a	type	of	collecDon,	which,	according	
to	Oracle	documentaDon,	"models	an	unordered	set	of	
elements."	
– It	is	a	"mulDset":	like	a	relaDonal	table,	there	is	no	inherent	
order	to	its	elements,	and	duplicates	are	allowed/
significant.	
•  From	a	pracDcal	standpoint,	you	can	access	nested	
table	elements	through	an	integer	index.	
•  MULTISET	operators	allow	set-level	operaDons	on	
nested	tables.	
1	 Apple	
2	 Pear	
3	 Orange	
4	 Apricot	
CREATE OR REPLACE TYPE list_of_names_t IS TABLE OF NUMBER;
5	 Pear	
Unordered set
of elements
Integer index
also available
nested_table_example.sql
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	42	
Varrays	
• A	varray	(short	for	"variable	size	array)	is	a	
type	of	collecDon	that	has	an	upper	bound	on	
its	number	of	elements.		
• This	upper	limit	is	set	when	the	type	is	
defined,	but	can	also	be	adjusted	at	runDme.	
• Always	dense,	can	only	trim	from	end	of	
varray.	
• Other	than	that,	quite	similar	to	a	nested	
table.	
1	 Apple	
2	 Pear	
3	 Orange	
4	 Apricot	
CREATE OR REPLACE TYPE list_of_names_t IS VARRAY (5) OF NUMBER;
5	 Pear	
And no more
elements can fit in
this varray.
varray_example.sql
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	43	
Ini>alizing	Nested	Tables	and	Varrays	
• Before	you	can	use	a	nested	table,	it	must	be	iniDalized.	
– IniDalize	them	explicitly	with	a	constructor	funcDon,	same	name	as	type,	
provided	by	Oracle	
– Provide	a	list	of	values	or	iniDalize	it	as	empty.	
DECLARE
TYPE numbers_t IS VARRAY (5) OF NUMBER;
salaries numbers_t := numbers_t (100, 200, 300);
BEGIN
DECLARE
TYPE numbers_t IS TABLE OF NUMBER;
salaries numbers_t;
BEGIN
salaries := numbers_t (100, 200, 300);
Initialize in
execution
section
Initialize in
declaration with
values
DECLARE
TYPE numbers_t IS TABLE OF NUMBER;
salaries numbers_t := numbers_t ();
Initialize empty
in declaration
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	44	
Valid	TABLE	OF	and	VARRAY	datatypes	
• For	PL/SQL-defined	types,	you	can	create	nested	table	of	almost	any	PL/
SQL	or	SQL	datatype.	
– All	scalar	types,	including	Boolean;	collecDon	of	object	types;	collecDon	of	other	
collecDons	
• There	are	some	restricDons:	
– Cannot	have	a	TABLE	OF	or	VARRAY	OF	cursor	variables	or	excepDons.	
• Schema-level	types	can	only	use	SQL	datatypes.	
nt_table_of_invalid_types.sql
va_table_of_invalid_types.sql
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	45	
Collec>on	as	Column	Type	
• If	the	type	is	defined	at	schema	level,	it	can	be	used	as	a	column	type.	
– Must	also	provide	a	STORE	AS	clause.	
• The	order	of	elements	in	the	column	is	not	preserved.	
• Can	specify	storage	characterisDcs	of	collecDon.	
• See	separate	lesson	for	details	on	using	collecDons	in	SQL.	
CREATE TABLE family
(
surname VARCHAR2 (1000)
, parent_names parent_names_t
, children_names child_names_t
)
NESTED TABLE children_names
STORE AS parent_names_tbl [storage_clause]
NESTED TABLE parent_names
STORE AS children_names_tbl [storage_clause]
nested_table_example.sql
varray_example.sql
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	46	
Changing	Upper	Limit	on	Varray	
• You	specify	an	upper	limit	at	the	Dme	the	varray	type	is	define.	
• You	can	also	change	this	limit	at	runDme	with	an	ALTER	TYPE	command.	
ALTER TYPE my_varray_t MODIFY LIMIT 100 INVALIDATE
/
BEGIN
EXECUTE IMMEDIATE
'ALTER TYPE my_varray_t MODIFY LIMIT 100 CASCADE';
END;
/
varray_change_limit.sql
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	47	
Using	Collec>ons	Inside	SQL	
• Define	your	collecDon	type	at	the	schema	level	&	collecDons	
declared	with	that	type	can	be	referenced	in	the	SQL	layer.	
– As	a	column	in	a	relaDonal	table	
– By	selecDng	from	that	collecDon	in	a	SELECT	statement	(note:	as	of	12.1,	
you	can	do	this	with	associaDve	arrays	indexed	by	integer).	
• Oracle	offers	ways	to	"translate"	between	a	collecDon	format	
and	a	relaDonal	table	format.	
– TABLE:	collecDon	->	relaDonal	table	
– MULTISET:	relaDonal	table	->	collecDon
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	48	
Using	the	TABLE	Operator	
• Use	TABLE	to	work	with	data	in	a	collecDon	as	if	it	were	data	
in	a	database	table.	
– Oracle	refers	to	this	as	"un-nesDng".	
– Especially	useful	when	you	would	like	to	apply	SQL	operaDons	to	a	PL/
SQL	collecDon	(ie,	one	not	stored	in	a	database	table).	
• You	do	not	need	to	explicitly	CAST	the	collecDon.	
– Oracle	will	figure	out	the	type	automaDcally.	
collections_in_sql.sql
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	49	
Changing	collec>on	contents	with	TABLE	
• You	can	use	TABLE	to	query	the	contents	of	a	collecDon	inside	
SQL.	
• You	can	also	change	the	contents	of	a	nested	table	column	value	
with	TABLE.	
– But	varrays	have	to	be	changed	"en	masse"	–	the	while	varray	is	replace;	
cannot	modify	individual	elements.	
UPDATE TABLE (SELECT children_names
FROM family
WHERE surname = 'Feuerstein')
SET COLUMN_VALUE = 'Eli Silva'
WHERE COLUMN_VALUE = 'Eli'
/
nested_table_change.sql
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	50	
Using	the	MULTISET	Operator	
• MULTISET	is	the	inverse	of	TABLE,	converDng	a	set	of	table,	view,	
query)	into	a	VARRAY	or	nested	table.	
– Use	MULTISET	to	emulate	or	transform	relaDonal	joins	into	collecDons,	with	
potenDal	client-server	performance	impact.	
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;
END;
collections_in_sql_multiset.sql
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	51	
Non-Sequen>al	Indexing	
• SomeDmes	you	simply	want	to	add	items	to	the	end	of	a	list.	
– This	makes	sense	if	the	order	in	which	items	were	added	is	significant.	
• But	how	do	you	find	a	specific	element	in	the	list?	
– With	sequenDal	indexing,	you	have	to	scan	through	the	contents	to	find	a	match.	
• And	what	if	you	want	to	find	elements	in	a	collecDon	using	more	than	one	
"index"?	
– CollecDons	have	just	one	index.	Period.	
string_tracker0.*
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	52	
Taking	advantage	of	non-sequen>al	indexing	
• AssociaDve	arrays	can	be	sparse.	
– Certainly,	any	string-indexed	collecDon	is	not	sequenDally	filled.		
• Valid	index	values	for	an	associaDve	array	cover	a	very	wide	range	of	
integers.	
– Very	oten	primary	keys	of	tables	are	sequence-generated	integers	that	fall	within	
this	range.	
• Combine	these	two	features	and	you	have	a	powerful	and	relaDvely	simple	
mechanism	for	emulaDng	relaDonal	table	keys.	
collection_of_records.sql
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	53	
Emula>ng	Primary	Key	in	Collec>on	
• Many	tables	rely	on	sequence-generated	integer	values	for	their	primary	
keys.	
– It	is	possible	that	this	sequence	value	could	exceed	2**31-1,	but	it	is	rarely	the	case.	
• Primary	keys	generally	are	not	"densely"	allocated.		
– Sequences	are	allocated	in	groups,	rows	are	deleted.	
• These	scenarios	mesh	perfectly	with	the	features	of	an	integer-indexed	
associaDve	array.	
emulate_primary_key1.sql
emulate_primary_key2.sql
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	54	
"Mul>ple	Indexes"	on	a	Collec>on	
• Most	relaDonal	tables	have	mulDple	indexes	defined	in	order	to	opDmize	
query	performance	(for	various	WHERE	clauses).	
• What	if	I	need	to	do	the	same	thing	in	a	collecDon?	
• You	can	only	have	a	single	index	on	an	associaDve	array	(INDEX	BY...).	
– But	you	could	create	other	collecDons	that	serve	as	indexes	into	the	"original"	
collecDon.	
emulate_indexes.sql
genaa.sql
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	55	
Lots	of	ways	to	index	associa>ve	arrays	
• Prior	to	Oracle9i	Release	2,	you	could	only	index	by	
BINARY_INTEGER.	
• You	can	now	define	the	index	on	your	associaDve	array	to	be:	
– Any	sub-type	derived	from	BINARY_INTEGER	
– VARCHAR2(n),	where	n	is	between	1	and	32767	
– %TYPE	against	a	database	column	that	is	consistent	with	the	above	rules	
– A	SUBTYPE	against	any	of	the	above.	
• This	means	that	you	can	now	index	on	string	values!	(and	
concatenated	indexes	and...)
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	56	
Examples	of	New	TYPE	Variants	
• All	of	the	following	are	valid	TYPE	declaraDons	in	Oracle9i	Release	
2	and	higher	
– You	cannot	use	%TYPE	against	an	INTEGER	column,	because	INTEGER	is	not	a	
subtype	of	BINARY_INTEGER.	
DECLARE
TYPE array_t1 IS TABLE OF NUMBER INDEX BY BINARY_INTEGER;
TYPE array_t2 IS TABLE OF NUMBER INDEX BY PLS_INTEGER;
TYPE array_t3 IS TABLE OF NUMBER INDEX BY POSITIVE;
TYPE array_t4 IS TABLE OF NUMBER INDEX BY NATURAL;
TYPE array_t5 IS TABLE OF NUMBER INDEX BY VARCHAR2(64);
TYPE array_t6 IS TABLE OF NUMBER INDEX BY VARCHAR2(32767);
TYPE array_t7 IS TABLE OF NUMBER INDEX BY
employee.last_name%TYPE;
TYPE array_t8 IS TABLE OF NUMBER INDEX BY
types_pkg.subtype_t;
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	57	
Working	with	string-indexed	collec>ons	
• The	syntax	for	using	string	indexing	is	the	same.		
– And	all	the	same	methods	are	available.	
• But	the	type	of	data	returned	by	FIRST,	LAST,	NEXT	and	PRIOR	
methods	is	VARCHAR2.	
• The	longer	the	string	values,	the	more	Dme	it	takes	Oracle	to	
"hash"	or	convert	that	string	to	the	integer	that	is	actually	used	
as	the	index	value.	
– RelaDvely	small	strings,	say	under	100	characters,	do	not	incur	too	large	a	
penalty.	
assoc_array*.sql
assoc_array_perf.tst
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	58	
An	example	of	string	indexing	
• I	generate	test	code	and	declare	variables.	So	I	need	to	make	sure	that	I	do	not	
declare	the	same	variable	more	than	once.	
•  There	are	lots	of	ways	to	do	this,	but	string-indexed	collecDons	make	it	really	
easy!	
FOR indx IN 1 .. l_variables.COUNT
LOOP
If varname_already_used THEN
-- DO NOTHING
ELSE
add_variable_declaration;
mark_varname_as_used;
END IF;
END LOOP;
string_tracker0.*
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	59	
String	Tracker	without	string	indexing	
• Two	subprograms	are	needed....	
– string_in_use:	returns	TRUE	if	the	string	was	previously	used.	
– mark_as_used:	mark	the	specified	string	as	being	used.	
• Most	"obvious"	implementaDon:	add	each	string	to	a	list	of	used	strings.	
• Then	search	through	the	list	for	a	match.	
string_tracker0.*
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	60	
String	Tracker	with	string	indexing	
• Rather	than	add	each	string	to	a	list	of	used	strings,	why	not	use	the	string	
as	the	index?	
CREATE OR REPLACE PACKAGE BODY string_tracker
IS
TYPE used_aat IS TABLE OF BOOLEAN INDEX BY VARCHAR2(32767);
g_names_used used_aat;
FUNCTION string_in_use ( value_in IN VARCHAR2 )
RETURN BOOLEAN
IS BEGIN
RETURN g_names_used.EXISTS ( value_in );
END string_in_use;
PROCEDURE mark_as_used (value_in IN VARCHAR2) IS
BEGIN
g_names_used ( value_in ) := TRUE;
END mark_as_used;
END string_tracker;
string_tracker1.*
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	61	
Conver>ng	from	Integer	to	String	Indexing	
• Suppose	I	am	emulaDng	my	primary	key	in	a	collecDon	index	for	a	batch	
job.	
– Much	beLer	performance!		
– But	my	primary	key	values	are	approaching		
2**31-1	(the	maximum	allowed	in	an	collecDon).	
• Must	I	abandon	the	collecDon	technique?	
• No!	You	can	convert	to	a	string	index.	
– Now	the	only	limitaDon	is	the	number	of	elements	defined	in	the	collecDon.	
– You	are	much	more	likely	to	run	out	of	memory	before	you	get	anywhere	near	
2**31-1	elements.		
emulate_primary_key.sql
int_to_string_indexing.sql
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	62	
Mul>level	(a.k.a.,	Nested)	Collec>ons	
• A	mulDlevel	collecDon	type	is	a	type	whose	element	is,	directly	or	
indirectly,	another	collecDon.	
• Usages	for	mulDlevel	collecDons:	
– Model	normalized	data	structures	in	PL/SQL	collecDons	
– Emulate	mulDdimensional	arrays.	
• The	syntax	for	working	with	mulDlevel	collecDons	can	be	hard	to	parse	(in	
your	head).	
multilevel_collections.sql
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	63	
String	Tracker	Version	2	
• I	introduced	the	string_tracker	package	in	"Working	with	String-Indexed	
CollecDons."	
– Keeps	track	of	the	names	of	variables	already	generated	in	test	code.	
– That	worked	fine	for	a	single	list.	What	if	I	need	to	keep	track	of	mulDple	lists,	and	
lists	within	lists?	
• Let's	extend	the	first	version	to	support	mulDple	lists	by	using	a	string-
indexed,	mulD-level	collecDon.	A	list	of	lists....	
string_tracker1.*
string_tracker2.*
string_tracker3*.*
List	1:	1-10000	 List	2:	10001-20000	 List	2:	20001-30000	
The	hard	way:	segmenDng	one	big	collecDon
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	64	
Emulate	mul>dimensional	arrays	
• CollecDons	are	always	single	dimensioned.	
• But	a	collecDon	of	collecDons	is	"kinda	like"	a	two-dimensional	array.	
– You	can	extrapolate	from	there.	
CREATE OR REPLACE PACKAGE multdim
IS
TYPE dim1_t IS TABLE OF VARCHAR2 (32767)
INDEX BY PLS_INTEGER;
TYPE dim2_t IS TABLE OF dim1_t
INDEX BY PLS_INTEGER;
TYPE dim3_t IS TABLE OF dim2_t
INDEX BY PLS_INTEGER;
multdim*.*
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	65	
Complex	example:	four-levels	of	nes>ng	
• The	most	complicated	structure	I	ever	built	was	a	four-level	nested	
collecDon	structure.	
• I	used	it	to	build	a	uDlity	to	analyze	packages	for	potenDally	ambiguous	
overloading.	
• The	next	several	slides	explore	the	implementaDon.	
– It	is	too	complex	to	fully	explain	in	this	lesson.
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	66	
Mul>level	collec>ons	as	a	kind	of	
normalized	database	design	
• I	have	found	that	coming	up	with	the	right	model	of	mulDlevel	collecDons	
is	very	similar	to	normalizing	data	in	relaDonal	tables.	
– Avoid	redundancy	
– Accurately	reflect	relaDonships	
– Simply	resulDng	applicaDon	code	
• Let's	take	a	look	at	an	example	of	such	a	process.
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	67	
The	problem	of	ambiguous	overloading	
• Oddly	and	sadly,	it	is	possible	to	compile	overloadings	which	
are	not	usable.	
– You	see	an	obvious	example	below,	but	there	are	many	more	subtle	
circumstances,	usually	involving	defaulted	parameters.	
• So	I	will	build	a	uDlity	to	idenDfy	such	ambiguous	overloadings.	
But	how	can	I	do	this?	
BEGIN
salespkg.calc_total ('ABC');
END;
PACKAGE salespkg
IS
PROCEDURE calc_total (
dept_in IN VARCHAR2);
PROCEDURE calc_total (
dept_in IN CHAR);
END salespkg;
?
ambig_overloading.sql
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	68	
ALL_ARGUMENTS	to	the	rescue!	
• Parsing	is	too	complicated	for	me,	but	the	ALL_ARGUMENTS	
data	dicDonary	view	contains	informaDon	about	all	the	
arguments	of	all	the	procedures	and	funcDons	to	which	I	have	
access.		
– That	sounds	preLy	good!	
• As	usual,	Oracle	offers	us	a	whole	lot	of	pleasure,	mixed	with	a	
liLle	bit	of	pain.	
– The	organizaDon	of	data	in	ALL_ARGUMENTS	is	a	bit	bizarre,	plus	it	is	
incomplete,	necessitaDng	the	use	also	of	
DBMS_DESCRIBE.DESCRIBE_COLUMNS.	 all_arguments.tst
all_arguments.sql
allargs.*
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	69	
First	Inclina>on:	Same	Old,	Same	Old	
• All	right	then,	I	will	grab	all	the	informaDon	from	
ALL_ARGUMENTS	and	dump	it	into	a	collecDon	based	on	that	
view!	Very	easy...	
CREATE OR REPLACE PROCEDURE get_all_arguments (
package_in IN VARCHAR2)
IS
TYPE all_arguments_tt IS TABLE OF all_arguments%ROWTYPE
INDEX BY BINARY_INTEGER;
l_arguments all_arguments_tt;
BEGIN
FOR rec IN (
SELECT * FROM all_arguments
WHERE owner = USER AND package_name = package_in)
LOOP
l_arguments (SQL%ROWCOUNT) := rec;
END LOOP;
END;
Load it up!
Emulate the
view.
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	70	
Then	what?	Write	lots	of	code	to	interpret	
the	contents...	
• Which	programs	are	overloaded?	Where	does	one	overloading	end	and	
another	start?	
l_last_program all_arguments.object_name%TYPE;
l_is_new_program BOOLEAN := FALSE;
l_last_overload PLS_INTEGER := -1;
BEGIN
FOR indx IN l_arguments.FIRST ..
l_arguments.LAST
LOOP
IF l_arguments (indx).object_name !=
l_last_program
OR l_last_program IS NULL
THEN
l_last_program :=
l_arguments (indx).object_name;
l_is_new_program := TRUE;
do_new_program_stuff;
END IF;
...
IF l_arguments (indx).overload
!= l_last_overload
OR l_last_overload = -1
THEN
IF l_is_new_program
THEN
do_first_overloading_stuff;
ELSE
do_new_overloading_stuff;
END IF;
END IF;
END LOOP;
END;
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	71	
Discovery:	there	is	a	hierarchy	within	the	
ALL_ARGUMENTS	data!		
•  Each	program	has	zero	or	more	
overloadings,	each	overloading	has	
N	arguments,	and	each	argument	
can	have	mulDple	"breakouts"	(my	
term	-	applies	to	non-scalar	
parameters,	such	as	records	or	
object	types).	
RUN_TEST
SHOW_RESULTS
RESET_FLAGS
Program name
Overloading 1
Overloading 2
Overloading
Argument 1
Argument 2
Argument 3
Argument 4
Argument 5
Argument Breakout 1
Breakout 1
Breakout 2
Breakout 3
Breakout
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	72	
What	if	I	reflect	this	hierarchy	in		
a	mul>level	collec>on?	
• Have	to	build	from	the	boLom	up:	
TYPE breakouts_t IS TABLE OF all_arguments%ROWTYPE
INDEX BY PLS_INTEGER;
TYPE arguments_t IS TABLE OF breakouts_t
INDEX BY PLS_INTEGER;
TYPE overloadings_t IS TABLE OF arguments_t
INDEX BY PLS_INTEGER;
TYPE programs_t IS TABLE OF overloadings_t
INDEX BY all_arguments.object_name%type;
1. Set of rows from
ALL_ARGUMENTS
String-based index
2. All the "breakout" info
for a single argument
3. All the argument info
for a single overloading
4. All the overloadings for
a distinct program name
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	73	
Then	I	can	populate	it	very	easily	
• Assigning	a	single	record	to	the	"lowest"	level	also	defines	each	of	
the	upper	levels.	
• NoDce	the	automaDc	"SELECT	DISTINCT"	on	name	that	results!	
FOR rec IN (SELECT * FROM all_arguments)
LOOP
l_arguments (NVL (l_arguments.LAST, 0) + 1)
:= rec;
 
l_programs
(rec.object_name)
(NVL (rec.overload, 0))
(rec.position)
(rec.data_level) := rec;
END LOOP;
I can still do the
typical sequential
load.
But I will now also
add the multi-level
load in single
assignment
show_all_arguments.sp
show_all_arguments.tst
cc_smartargs.pkb/load_arguments
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	74	
And	then	I	can	"query"	the	contents	
with	a	minimum	of	code	
l_programs ('TOP_SALES') (2).EXISTS (0)
Is the TOP_SALES
program overloaded?
l_programs ('TOP_SALES') (2)(0)(0).datatype
l_programs ('TOP_SALES').COUNT > 1
Is the 2nd overloading
of TOP_SALES a
function?
What is the datatype
of the RETURN
clause of the 2nd
overloading of
TOP_SALES? And, of course, I know the beginning and end points of
each program, overloading, and argument. I just use the
FIRST and LAST methods on those collections!
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	75	
Conclusions	–	Nested	Collec>ons	
• The	nested	collecDon	is	a	powerful,	but	potenDally	very	
complicated,	feature	of	PL/SQL.	
• Used	correctly,	it	can	hide	complexity	in	the	underlying	data	
structure,	and	greatly	simplify	your	algorithms.	
• If	you	find	yourself	saying	"It	shouldn't	be	this	hard,"	take	a	look	
at	how	you	are	using	your	collecDons	(or	SQL).		
– Perhaps	mulDlevel	collecDons	can	come	to	the	rescue!
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	76	
Manipula>ng	Nested	Tables	as	Mul>sets	
• Nested	tables	are,	from	a	theoreDcal	standpoint,	"mulDsets."	
– There	is	no	inherent	order	to	the	elements.	
– Duplicates	are	allowed	and	are	significant.	
– RelaDonal	tables	are	mulDsets	as	well.	
• If	a	set	has	no	order,	then	it	has	no	index,	so	it	must	be	manipulated	as	a	
set.	
• In	Oracle	Database	10g,	Oracle	added	MULTISET	set	operators	to	
manipulate	the	contents	of	nested	tables	(only).	
– Use	in	both	PL/SQL	blocks	and	SQL	statements.
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	77	
Set-Oriented	Features	for	Nested	Tables	
• Determine	if...	
– two	nested	tables	are	equal/unequal		
– a	nested	table	has	duplicates,	and	remove	duplicates	
– one	nested	table	contains	another	
– an	element	is	a	member	of	a	nested	table	
• Perform	set	operaDons.	
– Join	contents	of	two	nested	tables:	MULTISET	UNION.	
– Return	common	elements	of	two	nested	tables	with	MULTISET	INTERSECT.	
– Take	away	the	elements	of	one	nested	table	from	another	with	MULTISET	EXCEPT	
(oddly,	not	MINUS).
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	78	
Check	for	equality	and	inequality	
• You	can	use	=	and	<>	to	compare	the	contents	of	two	nested	
tables.	
– But	watch	out!	NULLs	have	the	usual	disrupDve	impact.	
• This	is	an	enormous	advantage	over	wriDng	a	program	to	compare	
the	contents	of	two	collecDons.	
DECLARE
TYPE clientele IS TABLE OF VARCHAR2 (64);
group1 clientele := clientele ('Customer 1', 'Customer 2');
group2 clientele := clientele ('Customer 1', 'Customer 3');
group3 clientele := clientele ('Customer 3', 'Customer 1');
BEGIN
IF group1 = group2 THEN
DBMS_OUTPUT.put_line ('Group 1 = Group 2');
ELSE
DBMS_OUTPUT.put_line ('Group 1 != Group 2');
END IF;
END;
10g_compare.sql
10g_compare_nulls.sql
10g_compare_old.sql
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	79	Copyright	2000-2008			Steven	Feuerstein	-	Page	79	
Nested	table	duplicates	–	detec>on	and	removal	
• Use	the	SET	operator	to	work	with	disDnct	values,	and	determine	
if	you	have	a	set	of	disDnct	values.	
DECLARE
keep_it_simple strings_nt := strings_nt ();
BEGIN
keep_it_simple := SET (favorites_pkg.my_favorites);
favorites_pkg.show_favorites ('FULL SET', favorites_pkg.my_favorites);
p.l (favorites_pkg.my_favorites IS A SET, 'My favorites distinct?');
p.l (favorites_pkg.my_favorites IS NOT A SET,
'My favorites NOT distinct?');
favorites_pkg.show_favorites (
'DISTINCT SET', keep_it_simple);
p.l (keep_it_simple IS A SET, 'Keep_it_simple distinct?');
p.l (keep_it_simple IS NOT A SET, 'Keep_it_simple NOT distinct?');
END;
authors.pkg
10g_set.sql
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	80	
Determine	if	value	is	in	nested	table	
• Use	the	MEMBER	OF	syntax	to	determine	if	a	value	is	in	the	
nested	table.	
– Much	simpler	than	scanning	the	contents	of	a	collecDon.	
– Performs	an	equality	check	for	the	enDre	element;	you	cannot	compare	
individual	fields	of	records,	and	so	on.	
• The	implementaDon	in	SQL	itself	is	quite	slow.	
• Performance	in	PL/SQL	is	fast.	
in_clause.*
10g_member_of.sql
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	81	
Does	one	nested	table	contains	another?	
• The	SUBMULTISET	OF	operator	determines	if	all	the	elements	of	one	
nested	table	are	in	another.	
DECLARE
TYPE nested_typ IS TABLE OF NUMBER;
nt1 nested_typ := nested_typ (1, 2);
nt2 nested_typ := nested_typ (3, 2, 1);
BEGIN
IF nt1 SUBMULTISET OF nt2
THEN
...
END IF;
END;
authors.pkg
10g_submultiset.sql
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	82	
UNION	two	nested	tables	together	
• Use	UNION	to	join	together	the	contents	of	two	nested	tables.	
• Duplicates	are	preserved	unless	you	include	the	DISTINCT	
modifier.	
– This	is	the	opposite	of	SQL	UNION	and	UNION	ALL.	
• The	resulDng	collecDon	is	either	empty	or	sequenDally	filled	from	
index	value	1.	
– You	do	not	need	to	iniDalize	or	extend	first.	
authors.pkg
10g_union.sql
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	83	
Intersect	two	nested	tables	together	
• Use	INTERSECT	to	find	the	common	elements	of	two	nested	tables.	
• Duplicates	are	preserved	unless	you	include	the	DISTINCT	modifier.	
– And	the	ALL	modifier	is	the	default.	
• The	resulDng	collecDon	is	either	empty	or	sequenDally	filled	from	index	
value	1.	
– You	do	not	need	to	iniDalize	or	extend	first.	
10g_intersect.sql
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	84	
Take	away	the	elements	of	one	nested	table	
from	another	
• Use	EXCEPT	(not	MINUS!)	to	take	all	elements	in	one	nested	table	out	of	
another.	
• Duplicates	are	preserved	unless	you	include	the	DISTINCT	modifier.	
– And	the	ALL	modifier	is	the	default.	
• The	resulDng	collecDon	is	either	empty	or	sequenDally	filled	from	index	
value	1.	
– You	do	not	need	to	iniDalize	or	extend	first.	
10g_except.sql
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	85	
Conclusions	–	MULTISET	operators		
• When	you	need	to	manipulate	the	contents	of	a	collecDon	as	a	
set,	use	a	nested	table.	
• The	MULTISET	operators	offer	a	powerful,	simple	way	to	avoid	
wriDng	lots	of	code.	
• The	SET,	SUBMULTISET	and	MEMBER	operators	also	can	come	in	
very	handy.	
• Watch	out	for	results	when	your	nested	table	may	contain	NULL	
elements.
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	86	
Collec>on	Best	Prac>ces	
• Watch	the	PGA	memory.	
• Hide	the	implementaDon	details.	
• Use	subtypes	to	self-document	element	and	index	by	types.	
• Choosing	the	best	type	of	collecDon
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	87	
Watch	PGA	memory	consump>on	
• Memory	for	collecDons	is	allocated	from	the	PGA	(Process	Global	
Area).	
– There	is	a	PGA	for	each	session	connected	to	the	instance.	
• Large	collecDons	constructed	by	programs	run	by	many	
simultaneously-connected	users	can	cause	memory	errors.	
• Use	the	plsq_memory	package	to	analyze	the	amount	of	memory	
used.	
– Or	at	least	make	sure	your	DBA	knows	you	are	making	extensive	use	of	
collecDons.	
plsql_memory*.*
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	88	
Hide	the	implementa>on	details.	
• CollecDons,	especially	mulD-level	collecDons,	are	very	complex	
structures.	
• Hide	the	way	you	to	set	and	get	elements	in	a	collecDon	behind	
an	API.	
– Procedure	to	set,	funcDon	to	get.	
– When	you	have	change	the	implement,	you	change	in	one	place	(single	point	
of	definiDon).	
multdim.sql
cc_smartargs.sql
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	89	
Use	subtypes	to	self-document	datatypes	
• You	should	avoid	using	base	datatypes	in	both	the	TABLE	OF	and	INDEX	BY	
clauses.	
– Especially	when	working	with	string-indexed	associaDve	arrays.	
• Use	SUBTYPEs	to	provide	applicaDon-specific	names	that	self-document	
both	contents	and	intenDon.	
– Constants	can	also	come	in	handy.	
string_tracker3.*
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	90	
Choosing	the	best	type	of	collec>on	
• Use	associaDve	arrays	when	you	need	to...	
– Work	within	PL/SQL	code	only	
– Sparsely	fill	and	manipulate	the	collecDon	
– Take	advantage	of	negaDve	index	values	or	string	indexing	
• Use	nested	tables	when	you	need	to...	
– Access	the	collecDon	inside	SQL	
– Want	or	need	to	perform	high	level	set	operaDons	(MULTISET)	
• Use	varrays	when	you	need	to...	
– If	you	need	to	specify	a	maximum	size	to	your	collecDon	
– OpDmize	performance	of	storing	collecDon	as	column
Copyright	©	2015	Oracle	and/or	its	affiliates.	All	rights	reserved.		|	 Page	91	
Collec>ons:	Don't	start	coding	without	them.	
• It	is	impossible	to	write	efficient,	high	quality	PL/SQL	code,	
taking	full	advantage	of	new	features,	unless	you	use	collecDons.	
– From	array	processing	to	table	funcDons,	collecDons	are	required.	
• Learn	collecDons	thoroughly	and	apply	them	throughout	your	
backend	code.	
– Your	code	will	get	faster	and	in	many	cases	much	simpler	than	it	might	have	
been	(though	not	always!).
92

All About PL/SQL Collections