Steven Feuerstein - Programming With Collections
Steven Feuerstein - Programming With Collections
PL/SQL Obsession
http://www.ToadWorld.com/SF
Download and use any of my scripts (examples, performance scripts, reusable code) from the same location: the demo.zip file. filename_from_demo_zip.sql You have my permission to use all these materials to do internal trainings and build your own applications.
But remember: they are not production ready. Modify them to fit your needs and then test them!
Copyright 2000-2006 Steven Feuerstein - Page 2
What is a collection?
1 abc 2 def 3 sf 4 q
...
22 rrr
23 swq
A collection is an "ordered group of elements, all of the same type." (PL/SQL User Guide and Reference)
That's a very general definition; from collections, you can build queues, stacks, lists, sets, arrays. Collections are single-dimensional and homogeneous, but you can emulate multi-dimensional structures.
Collections are a critical feature in many of the newest and most important features of PL/SQL.
Yet they are greatly underutilized by PL/SQL developers.
Copyright 2000-2006 Steven Feuerstein - Page 4
Serve up complex datasets of information to nonPL/SQL host environments using table functions. Dramatically improve multi-row querying, inserting, updating and deleting the contents of tables.
Combined with BULK COLLECT and FORALL....
Emulate bi-directional, random access cursors. Avoid mutating table trigger errors.
Copyright 2000-2006 Steven Feuerstein - Page 5
Sparse
Data does not have to be stored in consecutive rows, as is required in traditional 3GL arrays and VARRAYs.
Part of object model, requiring initialization. Is always dense initially, but can become sparse after deletes.
Can be defined as a schema level type and used as a relational table column type.
Copyright 2000-2006 Steven Feuerstein - Page 8 nested_table_example.sql
About Varrays
Has a maximum size, associated with its type.
Can adjust the size at runtime in Oracle10g R2.
Is always dense; you can only remove elements from the end of a varray.
Can be defined as a schema level type and used as a relational table column type.
varray_example.sql
Use the NOCOPY hint to reduce overhead of passing collections in and out of program units. Encapsulate or hide details of collection management. Don't always fill collections sequentially. Think about how you need to manipulate the contents. Try to read a row that doesn't exist, and Oracle raises NO_DATA_FOUND.
nocopy*.* Copyright 2000-2006 Steven Feuerstein - Page 12
Library cache
Update emp Set sal=...
Large Pool
calc_totals
show_emps
upd_salaries
Session 1
Session 2
plsql_memory*.*
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 plsql_memory.pkg
plsql_memory_demo.sql
Copyright 2000-2006 Steven Feuerstein - Page 14
This means that you can now index on string values! (and concatenated indexes and...)
Copyright 2000-2006 Steven Feuerstein - Page 15
If you are indexing by integer and find that your values are getting close to the limits (2**31 - 1 or -2**31 + 1), convert to a string assoc_array*.sql index.
Copyright 2000-2006 Steven Feuerstein - Page 17
There are lots of ways to do this, but string-indexed collections make it really easy!
FOR indx IN 1 .. l_variables.COUNT LOOP If varname_already_used THEN -- DO NOTHING ELSE
PROCEDURE mark_as_used (value_in IN maxvarchar2_t) IS BEGIN g_names_used ( value_in ) := TRUE; END mark_as_used; END string_tracker;
Oracle9i
Multi-level Collections
Prior to Oracle9i, you could have collections of records or objects, but only if all fields were scalars.
A collection containing another collection was not allowed.
Now you can create collections that contain other collections and complex types.
Applies to all three types of collections.
Let's extend the first version to support multiple lists by using a string-indexed, multilevel collection.
A list of lists....
Oracle10g
Oracle10g
Just use the basic operators.and NULLs have the usual disruptive impact.
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; IF group2 != group3 THEN DBMS_OUTPUT.put_line ('Group 2 != Group 3'); ELSE DBMS_OUTPUT.put_line ('Group 2 = Group 3'); END IF; END; Copyright 2000-2008 Steven Feuerstein - Page 26
Oracle10g
SQL: UNION
SQL: INTERSECT
SQL: MINUS
Oracle10g
Use the SET operator to work with distinct values, and determine if you have a set of distinct 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; 10g_set.sql 10g_favorites.pkg
GTTs still require interaction with the SGA. So collections will still be faster, but they will use more memory.
GTTs consume SGA memory.
global_temp_tab_vs_coll.sql Copyright 2000-2008 Steven Feuerstein - Page 29
Applying Collections
Data caching using packaged data Turbo-charged SQL with BULK COLLECT and FORALL Table functions I offer light coverage of these topics, simply to ensure that you know what is possible.
Why query information from the database (SGA) if that data does not change during your session?
Trivial example: the USER function More interesting: static tables
Application
PGA
Function
Application Requests Data
Subsequent accesses
Data found in cache. Database is not needed.
Database
Application
PGA
Function
Application Requests Data
bulk_rowcount.sql bulkexc.sql
bulkcoll.sql
WARNING! BULK COLLECT will not raise NO_DATA_FOUND if no rows are found. Always check contents of collection to confirm that something was retrieved. Copyright 2000-2006 Steven Feuerstein - Page 36
Use the LIMIT clause with the INTO to manage the amount of memory used with the BULK COLLECT operation.
TYPE emp_tt IS TABLE OF emps_in_dept_cur%ROWTYPE; emps emp_tt; BEGIN OPEN emps_in_dept_cur; LOOP FETCH emps_in_dept_cur BULK COLLECT INTO emps LIMIT 100;
EXIT WHEN emps.COUNT = 0; process_emps (emps); END LOOP; END bulk_with_limit; Copyright 2000-2006 Steven Feuerstein - Page 37
WARNING! BULK COLLECT will not raise NO_DATA_FOUND if no rows are found. Best to check contents of collection to confirm that something was retrieved.
bulklimit.sql
Combined with REF CURSORs, you can now more easily transfer data from within PL/SQL to host environments.
Java, for example, works very smoothly with cursor variables
Copyright 2000-2006 Steven Feuerstein - Page 38
Simple table function example Return a list of names as a nested table, and then call that function in the FROM clause.
CREATE OR REPLACE FUNCTION lotsa_names ( base_name_in IN VARCHAR2, count_in IN INTEGER ) RETURN names_nt IS retval names_nt := names_nt (); BEGIN retval.EXTEND (count_in); FOR indx IN 1 .. count_in LOOP retval (indx) := base_name_in || ' ' || indx; END LOOP; RETURN retval; END lotsa_names; Copyright 2000-2006 Steven Feuerstein - Page 39
SELECT column_value FROM TABLE ( lotsa_names ('Steven' , 100)) names; COLUMN_VALUE -----------Steven 1 ... Steven 100
tabfunc_scalar.sql
tabfunc_streaming.sql
BEGIN INSERT INTO tickertable SELECT * FROM TABLE (stockpivot (CURSOR (SELECT * FROM stocktable))); END; /
Copyright 2000-2006 Steven Feuerstein - Page 41
tabfunc_streaming.sql
Pipelined functions allow you to return data iteratively, asynchronous to termination of the function.
As data is produced within the function, it is passed back to the calling process/query.
RETURN...nothing at all!
Today I offer this challenge: learn collections 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 2000-2009 Steven Feuerstein - Page 46