0% found this document useful (0 votes)
33 views92 pages

Allaboutplsqlcollections 170303143509

Uploaded by

danko.banankno
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
33 views92 pages

Allaboutplsqlcollections 170303143509

Uploaded by

danko.banankno
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 92

All About

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

Large Pool calc_totals show_emps upd_salaries

emp_rec emp%rowtype; emp_rec emp%rowtype;


Session 1 tot_tab tottabtype; tot_tab tottabtype; Session 2
Session 1 memory (PGA/UGA) Session 2 memory (PGA/UGA)

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;

• 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.
Copyright © 2015 Oracle and/or its affiliates. All rights reserved. | Page 6
Calcula>ng PGA and UGA Consump>on
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')

• 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;

Nested table type


TYPE coll_name IS TABLE OF element_type;
Varray type
TYPE coll_name IS VARRAY (limit) OF element_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);

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

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

• 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

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)

GRANT EXECUTE ON strings_t TO PUBLIC

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

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;

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 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;

Copyright © 2015 Oracle and/or its affiliates. All rights reserved. |


plsqlloops.sp Page 24
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 PLS_INTEGER := my_collection.LAST;
rowind := my_collection.NEXT (rowind); BEGIN
END LOOP; LOOP
END; 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 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;

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

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;

• 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 1 Apple
element that holds the corresponding value. 22 Pear
• You can access elements without knowing their posiDons 100 Orange
in the array, and without traversing the array.

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;

• 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.
Copyright © 2015 Oracle and/or its affiliates. All rights reserved. | Page 34
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 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;

• A nested table is a type of collecDon, which, according Unordered set


to Oracle documentaDon, "models an unordered set of of elements
elements."
– It is a "mulDset": like a relaDonal table, there is no inherent 1 Apple
order to its elements, and duplicates are allowed/
2 Pear
significant.
3 Orange
• From a pracDcal standpoint, you can access nested 4 Apricot
table elements through an integer index. 5 Pear
• MULTISET operators allow set-level operaDons on
nested tables. Integer index
also available
nested_table_example.sql Copyright © 2015 Oracle and/or its affiliates. All rights reserved. | Page 41
Varrays
CREATE OR REPLACE TYPE list_of_names_t IS VARRAY (5) OF NUMBER;

• A varray (short for "variable size array) is a


type of collecDon that has an upper bound on 1 Apple
its number of elements. 2 Pear
3 Orange
• This upper limit is set when the type is
4 Apricot
defined, but can also be adjusted at runDme. 5 Pear
• Always dense, can only trim from end of And no more
varray. elements can fit in
this varray.
• Other than that, quite similar to a nested
table.
varray_example.sql
Copyright © 2015 Oracle and/or its affiliates. All rights reserved. | Page 42
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 TABLE OF NUMBER;
salaries numbers_t := numbers_t ();
Initialize empty
in declaration
DECLARE
TYPE numbers_t IS VARRAY (5) OF NUMBER; Initialize in
salaries numbers_t := numbers_t (100, 200, 300);
BEGIN declaration with
values
DECLARE
TYPE numbers_t IS TABLE OF NUMBER;
salaries numbers_t;
Initialize in
BEGIN execution
salaries := numbers_t (100, 200, 300);
section
Copyright © 2015 Oracle and/or its affiliates. All rights reserved. | Page 43
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 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.

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 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;

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;

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

The hard way: segmenDng one big collecDon string_tracker1.*


List 1: 1-10000 List 2: 10001-20000 List 2: 20001-30000 string_tracker2.*
string_tracker3*.*

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.

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

overloadings, each overloading has


Argument 2
N arguments, and each argument Breakout 1
Argument 3
can have mulDple "breakouts" (my Breakout 2
Argument 4
term - applies to non-scalar Breakout 3
Argument 5
parameters, such as records or
object types).

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:

1. Set of rows from TYPE breakouts_t IS TABLE OF all_arguments%ROWTYPE


ALL_ARGUMENTS INDEX BY PLS_INTEGER;
2. All the "breakout" info TYPE arguments_t IS TABLE OF breakouts_t
for a single argument INDEX BY PLS_INTEGER;
3. All the argument info
TYPE overloadings_t IS TABLE OF arguments_t
for a single overloading
INDEX BY PLS_INTEGER;
4. All the overloadings for
a distinct program name TYPE programs_t IS TABLE OF overloadings_t
INDEX BY all_arguments.object_name%type;

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.

l_programs But I will now also


(rec.object_name) add the multi-level
(NVL (rec.overload, 0)) load in single
(rec.position)
assignment
(rec.data_level) := rec;
END LOOP;
show_all_arguments.sp
show_all_arguments.tst
Copyright © 2015 Oracle and/or its affiliates. All rights reserved. |
cc_smartargs.pkb/load_arguments Page 73
And then I can "query" the contents
with a minimum of code
Is the TOP_SALES
program overloaded? l_programs ('TOP_SALES').COUNT > 1

Is the 2nd overloading


of TOP_SALES a l_programs ('TOP_SALES') (2).EXISTS (0)
function?

What is the datatype


of the RETURN
clause of the 2nd l_programs ('TOP_SALES') (2)(0)(0).datatype
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 74
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 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 ('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); 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;

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 81
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 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

You might also like