Allaboutplsqlcollections 170303143509
Allaboutplsqlcollections 170303143509
PL/SQL Collections
Steven Feuerstein
Oracle Developer Advocate for PL/SQL
Oracle CorporaDon
Email: [email protected]
TwiLer: @sfonplsql
Blog: stevenfeuersteinonplsql.blogspot.com
YouTube: PracDcally Perfect PL/SQL
Copyright © 2015 Oracle and/or its affiliates. All rights reserved. | 1
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 2
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 3
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" 1 Apple
22 Pear
• CollecDons are similar to single-dimensional
100 Orange
arrays in other programming languages.
– With lots of subtle differences, as well.
• CollecDons almost always consume Process 10023 Apricot
Global Area memory.
• CollecDons should be a "go to" datatype for
Oracle Database developers
Copyright © 2015 Oracle and/or its affiliates. All rights reserved. | Page 4
PL/SQL in Shared Memory
System Global Area (SGA) of RDBMS Instance
Shared Pool Library cache
Reserved Pool Shared SQL Select * Update emp
Pre-parsed from emp Set sal=...
Copyright © 2015 Oracle and/or its affiliates. All rights reserved. | Page 5
How PL/SQL uses the SGA, PGA and UGA
PACKAGE Pkg is
Nonstatic_Constant CONSTANT PLS_INTEGER := My_Sequence.Nextval;
Static_Constant CONSTANT PLS_INTEGER := 42;
END Pkg;
• 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.
BEGIN show_pga_uga.sql
plsql_memory.start_analysis; grantv$.sql
run_my_application; plsql_memory.pkg
plsql_memory.show_memory_usage; plsql_memory_demo.sql
END;
Copyright © 2015 Oracle and/or its affiliates. All rights reserved. | Page 7
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 8
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 9
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 10
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 11
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.
Associa>on array type
TYPE coll_name IS TABLE OF element_type INDEX BY index_type;
Copyright © 2015 Oracle and/or its affiliates. All rights reserved. | Page 12
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 13
Local collec>on types
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 14
Package-level Types
PACKAGE my_types
IS
TYPE strings_t IS TABLE OF VARCHAR2(100);
Copyright © 2015 Oracle and/or its affiliates. All rights reserved. | colltypes.pks Page 15
Schema-level Types
CREATE OR REPLACE TYPE strings_t IS TABLE OF VARCHAR2(100)
Copyright © 2015 Oracle and/or its affiliates. All rights reserved. | Page 16
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;
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 17
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 18
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 19
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 20
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 21
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 22
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 23
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
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 26
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 27
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;
Copyright © 2015 Oracle and/or its affiliates. All rights reserved. | extend.sql Page 29
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 30
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 31
Associa>ve Arrays
DECLARE
TYPE list_of_names_t IS TABLE OF employees.last_name%TYPE
INDEX BY PLS_INTEGER;
10023 Apricot
Copyright © 2015 Oracle and/or its affiliates. All rights reserved. | Page 32
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 33
Characteris>cs of Associa>ve Arrays
DECLARE
TYPE list_of_names_t IS TABLE OF employees.last_name%TYPE
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 35
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 36
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 37
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.
Copyright © 2015 Oracle and/or its affiliates. All rights reserved. | aa_limits.sql Page 38
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 39
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 40
Nested Tables
CREATE OR REPLACE TYPE list_of_names_t IS TABLE OF NUMBER;
nt_table_of_invalid_types.sql
va_table_of_invalid_types.sql
Copyright © 2015 Oracle and/or its affiliates. All rights reserved. | Page 44
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 nested_table_example.sql
STORE AS parent_names_tbl [storage_clause]
varray_example.sql
NESTED TABLE parent_names
STORE AS children_names_tbl [storage_clause]
Copyright © 2015 Oracle and/or its affiliates. All rights reserved. | Page 45
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.
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 46
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 47
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 48
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 49
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; collections_in_sql_multiset.sql
FETCH bird_curs into bird_row;
END;
Copyright © 2015 Oracle and/or its affiliates. All rights reserved. | Page 50
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 51
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 52
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 53
"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 54
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 55
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 56
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 57
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.
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;
• There are lots of ways to do this, but string-indexed collecDons make it really
easy!
string_tracker0.*
Copyright © 2015 Oracle and/or its affiliates. All rights reserved. | Page 58
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 59
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;
Copyright © 2015 Oracle and/or its affiliates. All rights reserved. | string_tracker1.* Page 60
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 61
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 62
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....
Copyright © 2015 Oracle and/or its affiliates. All rights reserved. | Page 63
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.
Copyright © 2015 Oracle and/or its affiliates. All rights reserved. | Page 64
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 65
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 66
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?
PACKAGE salespkg
IS
?
PROCEDURE calc_total ( BEGIN
dept_in IN VARCHAR2); salespkg.calc_total ('ABC');
PROCEDURE calc_total ( END;
dept_in IN CHAR);
END salespkg;
ambig_overloading.sql
Copyright © 2015 Oracle and/or its affiliates. All rights reserved. | Page 67
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 68
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; Emulate the
l_arguments all_arguments_tt; view.
BEGIN
FOR rec IN (
SELECT * FROM all_arguments
WHERE owner = USER AND package_name = package_in)
LOOP Load it up!
l_arguments (SQL%ROWCOUNT) := rec;
END LOOP;
END;
Copyright © 2015 Oracle and/or its affiliates. All rights reserved. | Page 69
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; IF
l_arguments (indx).overload
BEGIN != l_last_overload
FOR indx IN l_arguments.FIRST .. OR l_last_overload = -1
l_arguments.LAST THEN
LOOP IF l_is_new_program
IF l_arguments (indx).object_name != THEN
l_last_program do_first_overloading_stuff;
OR l_last_program IS NULL ELSE
THEN do_new_overloading_stuff;
l_last_program := END IF;
l_arguments (indx).object_name; END IF;
l_is_new_program := TRUE; END LOOP;
do_new_program_stuff; END;
END IF;
...
Copyright © 2015 Oracle and/or its affiliates. All rights reserved. | Page 70
Discovery: there is a hierarchy within the
ALL_ARGUMENTS
Program name
data!
RUN_TEST
SHOW_RESULTS Overloading
RESET_FLAGS Overloading 1
Overloading 2 Breakout
Argument
• Each program has zero or more Argument 1
Breakout 1
Copyright © 2015 Oracle and/or its affiliates. All rights reserved. | Page 71
What if I reflect this hierarchy in
a mul>level collec>on?
• Have to build from the boLom up:
String-based index
Copyright © 2015 Oracle and/or its affiliates. All rights reserved. | Page 72
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)
I can still do the
LOOP
l_arguments (NVL (l_arguments.LAST, 0) + 1) typical sequential
:= rec; load.
Copyright © 2015 Oracle and/or its affiliates. All rights reserved. | Page 75
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 76
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 77
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 10g_compare.sql
DBMS_OUTPUT.put_line ('Group 1 != Group 2'); 10g_compare_nulls.sql
END IF; 10g_compare_old.sql
END; Copyright © 2015 Oracle and/or its affiliates. All rights reserved. | Page 78
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 (
'DISTINCT SET', keep_it_simple); authors.pkg
10g_set.sql
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;
Copyright 2000-2008 Steven Feuerstein - Page 79 Copyright © 2015 Oracle and/or its affiliates. All rights reserved. | Page 79
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 80
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;
authors.pkg
10g_union.sql
Copyright © 2015 Oracle and/or its affiliates. All rights reserved. | Page 82
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 83
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 84
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 85
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 86
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 87
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 88
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 89
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 90
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!).
Copyright © 2015 Oracle and/or its affiliates. All rights reserved. | Page 91
92