Sie sind auf Seite 1von 148

Oracle PL/SQL Programming

Anti-Pattern
PL/SQL
Programming

Steven Feuerstein
steven@stevenfeuerstein.com
www.StevenFeuerstein.com
www.PLSQLChallenge.com

Copyright 2013 - Feuerstein and Associates, all rights reserved


Oracle PL/SQL Programming

What's an Anti-Pattern?
Design Patterns in software recognizes reality:
most of the code we write follows a pattern.
Different entities, same logic.
Define the pattern - and the patterned
solution - and we can code more productively
and confidently.
Anti-patterns are negative patterns.
Common but wrong ways to write code.
And a great learning device!
Copyright 2013 Feuerstein and Associates Page 2
Oracle PL/SQL Programming

How the Anti-Pattern Class Works


I present on an area of PL/SQL functionality.
You listen and sort-of learn. :-)
Then I let you loose on some anti-patterns.
Start with bad code, rewrite it to good code.
We will use the PL/SQL Challenge (plsqlchallenge.com).
You have an account set up for your email address,
password same as email.
Log in and find the Anti-Pattern "quizzes" in the Play a Quiz
table.
Pick the appropriate quiz and then follow the directions.
Most important: execute the setup code.

Copyright 2013 Feuerstein and Associates Page 3


Oracle PL/SQL Programming

Anti-Pattern PL/SQL Agenda


PL/SQL fundamentals
Error raising and handling
Working with collections
Writing and running SQL in PL/SQL
Key maintainability techniques

Copyright 2013 Feuerstein and Associates Page 4


Oracle PL/SQL Programming

How to benefit most from this training


Watch, listen, ask questions, focus on concepts and principles.
Download and use any of my training materials:

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.
You must test them and modify them to fit your needs.

Copyright 2013 Feuerstein and Associates Page 5


Oracle PL/SQL Programming

Websites for PL/SQL Developers


www.plsqlchallenge.com
Daily PL/SQL quiz with weekly and
monthly prizes

www.plsqlchannel.com
27+ hours of detailed video training
on Oracle PL/SQL

www.stevenfeuerstein.com
Monthly PL/SQL newsletter

Copyright 2013 Feuerstein and Associates Page 6


Oracle PL/SQL Programming

PL/SQL Fundamentals
Compiler optimization
Context switching in Oracle
Memory management

Copyright 2013 Feuerstein and Associates Page 7


Oracle PL/SQL Programming

The Optimizing Compiler


The PL/SQL compiler now has the ability to
automatically optimize your code.
The compiler rearranges your code.
Compile time increases, runtime performance improves.
You choose the level of optimization :
0 Pre-10g compilation without optimization
1 Smaller scale change, less impact on compile times
2 Most aggressive, maximum possible code transformations,
biggest impact on compile time. [default]
3 (Oracle11g) In-lining of local subprograms, in addition to all the
optimization performed at level 2
Stick with the default, unless you have a clear need
for an exception.
Copyright 2013 Feuerstein and Associates Page 8
Oracle PL/SQL Programming

The PL/SQL Optimizer: High Level View


The optimizer takes advantage of "freedoms" to re-
order the execution of statements.
In essence, changing the route that the runtime engine
takes to get from point A to point B in your code.
Some examples:
Unless otherwise specified, the operands of an expression
operator may be evaluated in any order.
Operands of a commutative operator may be commuted.
The actual arguments of a call or a SQL statement may be
evaluated in any order (including default actual
arguments).
Optimization does not change the logical behavior of
your code.
Optimization should not, for example, cause any of your
regression tests to suddenly fail!
Copyright 2013 Feuerstein and Associates 10g_optimize_cfl.sql Page 9
Oracle PL/SQL Programming

Context Switching in Oracle


We talk about the "backend", but it is not one
big thing - it is composed of many individual
processes and memory areas.
SQL statements are executed by the SQL
statement engine.
PL/SQL statements are executed by the PL/SQL
statement engine.
But PL/SQL calls SQL and SQL calls PL/SQL, so
what happens then?
Oracle switches the context.

Copyright 2013 Feuerstein and Associates Page 10


Oracle PL/SQL Programming

When PL/SQL Calls SQL.


Oracle Database

PL/SQL Runtime Engine SQL Engine


PL/SQL block
Procedural
statement
FOR rec IN emp_cur LOOP executor
UPDATE employee SQL
SET salary = ... statement
WHERE employee_id = executor
rec.employee_id;
END LOOP;

Performance penalty
for many context
switches
Copyright 2013 Feuerstein and Associates Page 11
Oracle PL/SQL Programming

And It Goes Both Ways


A query can call a PL/SQL function, which
contains a SELECT that calls a function.
CREATE OR REPLACE FUNCTION full_name (first_in IN VARCHAR2,
last_in IN VARCHAR2)
RETURN VARCHAR2 IS
BEGIN
RETURN first_in || ' ' || last_in;
END;
/

CREATE OR REPLACE FUNCTION emp_name (employee_id_in IN INTEGER)


RETURN VARCHAR2
IS
l_return VARCHAR2 (32767);
BEGIN
SELECT full_name (first_name, last_name)
INTO l_return FROM employees;
RETURN l_return;
END;
/

SELECT emp_name (employee_id) FROM employees


/
Copyright 2013 Feuerstein and Associates Page 12
Oracle PL/SQL Programming

Watch out for those Context Switches!


We will explore a number of techniques for
reducing the number and/or cost of context
switches in the class.
But the responsibility falls on you to make
sure that your code makes sense.
Watch out for flipping back and forth between
SQL and PL/SQL!
And now.our first exercise!

Copyright 2013 Feuerstein and Associates Page 13


Oracle PL/SQL Programming

Anti-Pattern PL/SQL Exercise Time!

PL/SQL Challenge - Play a Quiz

Anti-Pattern PL/SQL -
Context Switching

Copyright 2013 Feuerstein and Associates Page 14


Oracle PL/SQL Programming

Memory Management and PL/SQL Code


It is certainly possible to write PL/SQL code
that consumes so much memory, it kills a
user's session.
It's quite easy to do, in fact.
As you work with more advanced features, like
collections and FORALL, you will need to pay
attention to memory, and make adjustments.
Let's review how Oracle manages memory at
run-time.

Copyright 2013 Feuerstein and Associates memory_error.sql Page 15


Oracle PL/SQL Programming

PL/SQL in Shared Memory


System Global Area (SGA) of RDBMS Instance
Shared Pool Library cache

Shared SQL Select * Update emp


Reserved Pool
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 1 memory Session 2 memory Session 2


(PGA/UGA) (PGA/UGA)

Copyright 2013 Feuerstein and Associates Page 16


Oracle PL/SQL Programming

How PL/SQL uses the SGA, PGA and UGA


PACKAGE Pkg is /* 11g feature! */
Nonstatic_Constant CONSTANT PLS_INTEGER := My_Sequence.Nextval;
Static_Constant CONSTANT PLS_INTEGER := 42;
END Pkg;

The SGA contains information that can be shared


across sessions connected to the instance.
In PL/SQL, this is limited to package static 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 2013 Feuerstein and Associates Page 17


Oracle PL/SQL Programming

Calculating PGA and UGA Consumption


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 consumption 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 2013 Feuerstein and Associates Page 18


Oracle PL/SQL Programming

Tips for managing memory


Use LIMIT clause with BULK COLLECT.
Use varrays with BULK COLLECT to
declaratively guard against "memory creep."
Use NOCOPY hint when passing IN OUT
collections.
Be very careful about defining variables at
the package level.
Memory will not be released when the block
terminates.
bulklimit.sql
Use pipelined table functions. varray_collection_limit.sql
nocopy*.tst
tabfunc_pipelined.sql

Copyright 2013 Feuerstein and Associates Page 19


Oracle PL/SQL Programming

Error Management in PL/SQL


Things go wrong - all the time.
We need to anticipate problems and handle
problems.
Provide key information to users and record
diagnostic info for us to use to resolve problem.
Do that effectively, we need to leverage all the
features Oracle has to offer.
Exception handling architecture
Built-in functions

Copyright 2013 Feuerstein and Associates Page 20


Oracle PL/SQL Programming

Raising Exceptions
Oracle raises exceptions.
You can raise exceptions as well.
Use RAISE to raise an exception.
Use RAISE_APPLICATION_ERROR to pass an
application-specific message back to a user.
You can also define your own exceptions so
that you can raise that exception - and handle
it by name.
With PRAGMA EXCEPTION_INIT.
raise.sql
raise_application_error.sql
Copyright 2013 Feuerstein and Associates Page 21
Oracle PL/SQL Programming

Handling Exceptions
After exception is raised, the block closes and
control is passed to the exception section or
propagates unhandled to an outer block.
Exceptions raised in declaration section are not
handled.
The EXCEPTION section contains one or more
WHEN clauses.
WHEN exc - traps one exception
WHEN exc1 OR exc2 - traps more than one
WHEN OTHERS - traps almost any exception.
info_for_handling.sql
excquiz*.sql
Copyright 2013 Feuerstein and Associates Page 22
Oracle PL/SQL Programming

Impact of errors on DML execution


A single DML statement can result in changes to
multiple rows.
When an error occurs on a change to a row....
All previous changes from that statement are rolled
back.
No other rows are processed.
An error is passed out to the calling block (turns into a
PL/SQL exception).
No rollback on completed DML in that session.
Usually acceptable, but what if you want to:
Avoid losing all prior changes?
One way to do this with SAVE EXCEPTIONS
errors_and_dml.sql
Copyright 2013 Feuerstein and Associates continue_past_exceptions.sql Page 23
Oracle PL/SQL Programming

Anti-Pattern Exercise Time!

PL/SQL Challenge - Play a Quiz

Anti-Pattern PL/SQL -
Error Management Basics

Copyright 2013 Feuerstein and Associates Page 24


Oracle PL/SQL Programming

Oracle Built-ins For Handling Exceptions


In addition to the application-specific
information you may want to log, Oracle built-
ins provide you with answers to the following
questions:
How did I get here?
What is the error code?
What is the error message and/or stack?
On what line was the error raised?

Copyright 2013 Feuerstein and Associates Page 25


Oracle PL/SQL Programming

SQLCODE and SQLERRM


SQLCODE returns the error code of the most
recently-raised exception in your session.
SQLERRM returns the error message
associated with SQLCODE but it also a
generic error message lookup function.
Neither SQLCODE nor SQLERRM can be called
from within a SQL statement.
You must assign them to local variables to use their values
in SQL statements (like writing to an error log).

sqlcode.sql
sqlcode_test.sql
Copyright 2013 Feuerstein and Associates Page 26
Oracle PL/SQL Programming

SQLERRM Details
If you don't pass an argument to SQLERRM, it returns
the error message for the SQLCODE value.
When called outside of an exception handler, always
returns "success" message no error.
You can also pass an error code to SQLERRM and it
will return the generic error message.
The maximum size of a string returned by SQLERRM
is 512 bytes.
When there is a stack of errors, Oracle may truncate the
string returned by SQLERRM.
Oracle recommends you use
DBMS_UTILITY.FORMAT_ERROR_STACK instead.
sqlerrm.sql

Copyright 2013 Feuerstein and Associates Page 27


Oracle PL/SQL Programming

DBMS_UTILITY error functions


Answer the question "How did I get here?"
with DBMS_UTILITY.FORMAT_CALL_STACK.
Get a more complete error message with
DBMS_UTILITY.FORMAT_ERROR_STACK.
Find line number on which error was raised
with
DBMS_UTILITY.FORMAT_ERROR_BACKTRACE.

Copyright 2013 Feuerstein and Associates Page 28


Oracle PL/SQL Programming

DBMS_UTILITY.FORMAT_CALL_STACK
The "call stack" reveals the path taken through
your application code to get to that point.
Very useful whenever tracing or logging
errors.
The string is formatted to show line number
and program unit name.
But it does not reveal the names of subprograms
in packages.
callstack.sql
callstack.pkg

Copyright 2013 Feuerstein and Associates Page 29


Oracle PL/SQL Programming

DBMS_UTILITY.FORMAT_ERROR_STACK
This built-in returns the error stack in the
current session.
Possibly more than one error in stack.
Returns NULL when there is no error.
Returns a string of maximum size 2000 bytes
(according to the documentation).
Oracle recommends you use this instead of
SQLERRM, to reduce the chance of truncation.
errorstack.sql
big_error_stack.sql

Copyright 2013 Feuerstein and Associates Page 30


Oracle PL/SQL Programming

DBMS_UTILITY.FORMAT_ERROR_BACKTRACE
The backtrace function (new to 10.2) answers the
question: "Where was my error raised?
Prior to 10.2, you could not get this information from
within PL/SQL.
Call it whenever you are logging an error.
When you re-raise your exception (RAISE;) or raise a
different exception, subsequent BACKTRACE calls will
point to that line.
So before a re-raise, call BACKTRACE and store that
information to avoid losing the original line number.

backtrace.sql
bt.pkg
Copyright 2013 Feuerstein and Associates Page 31
Oracle PL/SQL Programming

The UTL_CALL_STACK Package (12.1)


Prior to 12c, you could obtain several kinds of
"stacks" through individual function calls:
DBMS_UTILITY.FORMAT_CALL_STACK - "How did I
get here?"
DBMS_UTILITY.FORMAT_ERROR_STACK - "What is
the error message/stack?"
DBMS_UTILITY.FORMAT_ERROR_BACKTRACE - "On
what line was my error raised?"
Now, the UTL_CALL_STACK package supports
all that and a much better API to the info in the
stack.
12c_utl_callstack*.sql
Copyright 2013 Feuerstein and Associates Page 32
Oracle PL/SQL Programming

Anti-Pattern Exercise Time!

PL/SQL Challenge - Play a Quiz

Anti-Pattern PL/SQL -
Error Management Built-Ins

Copyright 2013 Feuerstein and Associates Page 33


Oracle PL/SQL Programming

Working with PL/SQL Collections


Collections are single-dimensioned 1 Apple
lists of information, similar to 3GL 22 Pear
arrays. 100 Orange
Use them to maintain in-program
(PGA) listsvery fast!
10023 Apricot
Collections enable other key features
of PL/SQL
BULK COLLECT and FORALL use them to
boost multi-row SQL performance
Serve up complex datasets of
information to non-PL/SQL host
environments using table functions.

Copyright 2013 Feuerstein and Associates Page 34


Oracle PL/SQL Programming

Different Types of Collections


Three types of collections
Associative array - PL/SQL only datatype
Nested table and varray (varying arrays) - used in
PL/SQL and SQL
Use collection methods to navigate through
and manipulate collections.
Watch out for PGA consumption!

assoc_array_example.sql
nested_table_example.sql
varray_example.sql

Copyright 2013 Feuerstein and Associates Page 35


Oracle PL/SQL Programming

Methods that retrieve information


You invoke a method by attaching it, using dot
notation, to the name of the type/class or to an
instance of the class.
COUNT: number of elements defined in collection.
EXISTS: TRUE if the specified index values is defined.
FIRST/LAST: lowest/highest index values of defined rows.
NEXT/PRIOR: defined index value after/before the
specified index value .
LIMIT: max. number of elements allowed in a VARRAY.
DELETE deletes one or more rows from an associative
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.
collection_exists.sql
method_vs_proc.sql varray_limit.sql
Copyright 2013 Feuerstein and Associates plsqlloops.sp delete.sql extend.sql Page 36
Oracle PL/SQL Programming

Anti-Pattern Exercise Time!

PL/SQL Challenge - Play a Quiz

Anti-Pattern PL/SQL -
Collection Methods

Copyright 2013 Feuerstein and Associates Page 37


Oracle PL/SQL Programming

Creative Uses of Associative Array Indexes


Sometimes you add items to end of list.
The order added is significant.
But how do you find an element in the list?
What if the element is a record and you need
to find elements by different fields?
Associative arrays can help because.
You can index by integer or strings.
An associative array can be sparse.

collection_of_records.sql
string_tracker0.*
Copyright 2013 Feuerstein and Associates Page 38
Oracle PL/SQL Programming

Taking advantage of non-sequential indexing


Associative arrays can be sparse.
Certainly, any string-indexed collection is not
sequentially filled.
Valid index values for an associative array
cover a very wide range of integers.
Very often primary keys of tables are sequence-
generated integers that fall within this range.
Combine these two features and you have a
powerful and relatively simple mechanism for
emulating relational table keys.
collection_of_records.sql
Copyright 2013 Feuerstein and Associates Page 39
Oracle PL/SQL Programming

Emulating Primary Key in Collection


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

emulate_primary_key1.sql
emulate_primary_key2.sql
Copyright 2013 Feuerstein and Associates Page 40
Oracle PL/SQL Programming

"Multiple Indexes" on a Collection


Most relational tables have multiple indexes
defined in order to optimize query
performance (for various WHERE clauses).
What if I need to do the same thing in a
collection?
You can only have a single index on an
associative array (INDEX BY...).
But you could create other collections that serve
as indexes into the "original" collection.
emulate_indexes.sql
genaa.sql
Copyright 2013 Feuerstein and Associates Page 41
Oracle PL/SQL Programming

Working with string-indexed collections


The anti-pattern: code that iterates through a
collection, looking for a match on a value.
A "full collection scan" - they're fast, but they are
not necessary and they lead to code bloat.
Good to keep in mind:
FIRST, LAST, NEXT and PRIOR methods return
strings.
The longer the string values, the higher the overhead to
"hash" or convert that string to the integer that is actually
used as the index value.

assoc_array*.sql
assoc_array_perf.tst
Copyright 2013 Feuerstein and Associates string_tracker2.* Page 42
Oracle PL/SQL Programming

Anti-Pattern Exercise Time!

PL/SQL Challenge - Play a Quiz

Anti-Pattern PL/SQL -
Creative Indexing with Arrays

Copyright 2013 Feuerstein and Associates Page 43


Oracle PL/SQL Programming

Nested Collections
The element of a nested collection type is
itself a collection or contains a collection.
Field in record, attribute in object type
Usages for multilevel collections:
Model normalized data structures in PL/SQL
collections
Emulate multidimensional arrays.
The syntax for working with multilevel
collections can be hard to parse (in your
head).

nested_collections.sql
Copyright 2013 Feuerstein and Associates Page 44
Oracle PL/SQL Programming

String Tracker Version 3


We've already enhanced the String Tracker
package to avoid a full collection scan.
But what if we now need to keep track of
items in more than one list?
A "list of lists" does the job very nicely!
Or you could go "hard code" and "old school" by
segmenting one big collection. Ugh.
The hard way: segmenting one big collection
List 1: 1-10000 List 2: 10001-20000 List 2: 20001-30000

string_tracker3*.*
Copyright 2013 Feuerstein and Associates Page 45
Oracle PL/SQL Programming

Anti-Pattern Exercise Time!

PL/SQL Challenge - Play a Quiz

Anti-Pattern PL/SQL -
Nested Collections

Copyright 2013 Feuerstein and Associates Page 46


Oracle PL/SQL Programming

Conclusions Nested Collections


The nested collection is a powerful, but
potentially 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
collections (or SQL).
Perhaps nested collections can come to the rescue!

Copyright 2013 Feuerstein and Associates Page 47


Oracle PL/SQL Programming

Manipulating Nested Tables as Multisets


Nested tables are, from a theoretical standpoint,
"multisets."
There is no inherent order to the elements.
Duplicates are allowed and are significant.
Relational tables are multisets 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 2013 Feuerstein and Associates Page 48


Oracle PL/SQL Programming

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 operations.
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 2013 Feuerstein and Associates Page 49


Oracle PL/SQL Programming

Check for equality and inequality


You can use = and <> to compare the contents of two
nested tables.
But watch out! NULLs have the usual disruptive impact.
This is an enormous advantage over writing a program
to compare the contents of two collections.
DECLARE
TYPE clientele IS TABLE OF VARCHAR2 (64);
group1 clientele := clientele ('Customer 1', 'Customer 2');
group2 clientele := clientele ('Customer 1', 'Customer 3');
group3 clientele := clientele ('Customer 3', 'Customer 1');
BEGIN
IF group1 = group2 THEN
DBMS_OUTPUT.put_line ('Group 1 = Group 2');
ELSE
DBMS_OUTPUT.put_line ('Group 1 != Group 2');
END IF;
END;
10g_compare.sql
10g_compare_nulls.sql
Copyright 2000-2008 Steven Feuerstein - Page 50 10g_compare_old.sql
Copyright 2013 Feuerstein and Associates Page 50
Oracle PL/SQL Programming

Nested table duplicates detection and removal


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;
authors.pkg
Copyright 2000-2008 Steven Feuerstein - Page 51 10g_set.sql
Copyright 2013 Feuerstein and Associates Page 51
Oracle PL/SQL Programming

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
collection.
Performs an equality check for the entire element;
you cannot compare individual fields of records,
and so on.
The implementation in SQL itself is quite slow.
Performance in PL/SQL is fast.

in_clause.*
Copyright 2013 Feuerstein and Associates 10g_member_of.sql Page 52
Oracle PL/SQL Programming

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
Copyright 2013 Feuerstein and Associates 10g_submultiset.sql Page 53
Oracle PL/SQL Programming

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 resulting collection is either empty or
sequentially filled from index value 1.
You do not need to initialize or extend first.

authors.pkg
10g_union.sql
Copyright 2013 Feuerstein and Associates Page 54
Oracle PL/SQL Programming

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 resulting collection is either empty or
sequentially filled from index value 1.
You do not need to initialize or extend first.

10g_intersect.sql
Copyright 2013 Feuerstein and Associates Page 55
Oracle PL/SQL Programming

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 resulting collection is either empty or
sequentially filled from index value 1.
You do not need to initialize or extend first.

10g_except.sql
Copyright 2013 Feuerstein and Associates Page 56
Oracle PL/SQL Programming

Anti-Pattern Exercise Time!

PL/SQL Challenge - Play a Quiz

Anti-Pattern PL/SQL -
Nested Table Features

Copyright 2013 Feuerstein and Associates Page 57


Oracle PL/SQL Programming

Conclusions MULTISET operators


When you need to manipulate the contents of
a collection as a set, use a nested table.
The MULTISET operators offer a powerful,
simple way to avoid writing 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 2013 Feuerstein and Associates Page 58


Oracle PL/SQL Programming

Choosing the best type of collection


Use associative arrays when you need to...
Work within PL/SQL code only
Sparsely fill and manipulate the collection
Take advantage of negative index values or string indexing
Use nested tables when you need to...
Access the collection inside SQL (table functions, columns in
tables, or utilize SQL operations)
Want or need to perform high level set operations
(MULTISET)
Use varrays when you need to...
If you need to specify a maximum size to your collection
Optimize performance of storing collection as column

Copyright 2013 Feuerstein and Associates Page 59


Oracle PL/SQL Programming

Collections: 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 collections.
From array processing to table functions, collections are
required.
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!).

Hands-On Collection seminar


www.ToadWorld.com/SF

Copyright 2013 Feuerstein and Associates Page 60


Oracle PL/SQL Programming

SQL in PL/SQL
SQL: the heart and soul of any Oracle-based
application.
And also the problem child.
PLSQL: the best place to put your SQL
And the opportunity to create a slow, ugly mess
We'll start with some principles
Move on to key features for executing SQL
inside PL/SQL program units.

Copyright 2013 Feuerstein and Associates Page 61


Oracle PL/SQL Programming

Principles for Writing SQL in PL/SQL


Do as much as you can in SQL.
It is almost always going to be more efficient and
easier to maintain than an implementation in
PL/SQL.
Avoid repetition of the same SQL statement.
Multiple places in code
Multiple executions of same statement
Multiple retrievals of same (unchanged) data

Copyright 2013 Feuerstein and Associates Page 62


Oracle PL/SQL Programming

Fully leverage SQL in your PL/SQL code


Oracle continually adds significant new
functionality to the SQL language.
If you don't keep up with SQL capabilities, you
will write slower, more complicated PL/SQL
code than is necessary.
I am a good example of what you don't want to do
or how to be.
So take the time to refresh your
understanding of Oracle SQL in 10g and 11g.

Copyright 2013 Feuerstein and Associates Page 63


Oracle PL/SQL Programming

Some exciting recently added SQL features


Courtesy of Lucas Jellema of AMIS Consulting
Analytical Functions
Especially LAG and LEAD; these allow you to look to previous and following
rows to calculate differences.
WITH clause (subquery factoring)
Allows the definition of 'views' inside a query that can be used and reused;
they allow procedural top-down logic inside a query
Flashback query
No more need for journal tables, history tables, etc.
ANSI JOIN syntax
Replaces the (+) operator and introduces FULL OUTER JOIN
SYS_CONNECT_BY_PATH and CONNECT_BY_ROOT for hierarchical queries
Scalar subquery
select d.deptno
Adds a subquery to a query like a function call. , (select count(*)
from emp e where
e.deptno = d.deptno)
number_staff from dept

Copyright 2013 Feuerstein and Associates Page 64


Oracle PL/SQL Programming

"Load up" Your SQL


Put as much application logic into your SQL
statements, less in your PL/SQL code.
Below a very simple demonstration of the principle
Move the IF statement into the WHERE clause of the
query!

BEGIN
FOR employee_r IN ( SELECT last_name
FROM employees
ORDER BY employee_id)
LOOP
IF employee_r.last_name LIKE '%s%'
THEN
DBMS_OUTPUT.put_line (employee_r.last_name);
END IF;
END LOOP;
END;

Copyright 2013 Feuerstein and Associates Page 65


Oracle PL/SQL Programming

Anti-Pattern Exercise Time!

PL/SQL Challenge - Play a Quiz

Anti-Pattern PL/SQL
Maximize SQL

Copyright 2013 Feuerstein and Associates Page 66


Oracle PL/SQL Programming

Avoid Repetition of the Same SQL


Multiple places in code
Bad to repeat hard-coded literals, worse to repeat
the same SQL statement
Multiple executions of same statement
Unnecessary context switches; "switch" to bulk
processing.
Multiple retrievals of same (unchanged) data
Use caching features, especially the function
result cache.

Copyright 2013 Feuerstein and Associates Page 67


Oracle PL/SQL Programming

Multiple Places in Code


It's so easy to write SQL in PL/SQL that we
take it for granted, especially the "simple" and
"never changing" statements like.
CREATE OR REPLACE PROCEDURE do_stuff_with_employee (
employee_id_in IN employees.employee_id%TYPE)
IS
l_employee employees%ROWTYPE;
l_manager employees%ROWTYPE;
BEGIN
SELECT *
INTO l_employee
FROM employees
WHERE employee_id = employee_id_in;

/* Do some stuff... and then, again! */

SELECT *
INTO l_manager
FROM employees
WHERE employee_id = l_employee.manager_id;
END;
Copyright 2013 Feuerstein and Associates Page 68
Oracle PL/SQL Programming

Repetition of SQL -> Maint. Nightmare


If you do not put guidelines in place for when,
where and how to write SQL, your application
will be extremely difficult to maintain.
Solution: use a data access layer that isolates
SQL behind named objects, including views,
procedures and functions.
Then call the program unit or execute a query
against the view.

Copyright 2013 Feuerstein and Associates Page 69


Oracle PL/SQL Programming

Anti-Pattern Exercise Time!

PL/SQL Challenge - Play a Quiz

Anti-Pattern PL/SQL -
Same SQL Multiple Places

Copyright 2013 Feuerstein and Associates Page 70


Oracle PL/SQL Programming

Multiple Executions of Same Statement


Your SQL may only exist in one place - inside a
loop (usually a cursor FOR loop).
This means that the same statement is
executed, with only changes in bind variables.
This row-by-row processing results in many
context switches and corresponding
performance degradation.
Solution: switch to bulk processing (BULK
COLLECT and FORALL).

Copyright 2013 Feuerstein and Associates Page 71


Oracle PL/SQL Programming

Bulk Processing in PL/SQL


The goal is straightforward: reduce the number of
context switches and you improve performance.
To do this, Oracle "bundles up" the requests for data
(or to change data) and then passes them with a
single context switch.
FORALL speeds up DML.
Use with inserts, updates, deletes and merges.
Move data from collections to tables.
BULK COLLECT speeds up queries.
Can be used with all kinds of queries: implicit, explicit,
static and dynamic.
Move data from tables into collections.

Copyright 2013 Feuerstein and Associates Page 72


Oracle PL/SQL Programming

Bulk processing with FORALL


Oracle server

PL/SQL Runtime Engine SQL Engine


PL/SQL block
Procedural
FORALL indx IN statement
list_of_emps.FIRST..
list_of_emps.LAST
executor
SQL
UPDATE employee
SET salary = ...
statement
WHERE employee_id = executor
list_of_emps(indx);

Update... Update...
Update... Update...
Update... Update...
Update... Update...
Update... Update...
Update... Fewer context switches, Update...
same SQL behavior
Copyright 2013 Feuerstein and Associates Page 73
Oracle PL/SQL Programming

Impact of Bulk Processing in SQL layer


The bulk processing features of PL/SQL change the
way the PL/SQL engine communicates with the SQL
layer.
For both FORALL and BULK COLLECT, the processing
in the SQL engine is almost completely unchanged.
Same transaction and rollback segment management
Same number of individual SQL statements will be
executed.
Only one difference: BEFORE and AFTER statement-
level triggers only fire once per FORALL INSERT
statements.
Not for each INSERT statement passed to the SQL engine
from the FORALL statement.

statement_trigger_and_forall.sql
Copyright 2013 Feuerstein and Associates Page 74
Oracle PL/SQL Programming

BULK COLLECT for multi-row querying


SELECT * BULK COLLECT INTO collection(s) FROM table;

FETCH cur BULK COLLECT INTO collection(s);

EXECUTE IMMEDIATE query BULK COLLECT INTO collection(s);

Retrieve multiple rows into a collection with a


single fetch (context switch to the SQL engine).
Deposit the multiple rows of data into one or
more collections.

Copyright 2013 Feuerstein and Associates Page 75


Oracle PL/SQL Programming

"Good to Know" about BULK COLLECT


NO_DATA_FOUND is not raised when no rows are
fetched; instead, the collection is emptied.
The "INTO" collections are filled sequentially from
index value 1.
There are no "gaps" between 1 and the index value
returned by the COUNT method.
No need to initialize or extend nested tables and
varrays. Done automatically by Oracle.
Cursor FOR loops are automatically optimized to
execute at a performance similar to BULK
COLLECT.
Copyright 2013 Feuerstein and Associates Page 76
Oracle PL/SQL Programming

An "unlimited" BULK COLLECT


DECLARE
Declare a nested TYPE employees_aat IS TABLE OF
table of records to employees%ROWTYPE;
hold the queried
data. l_employees employees_aat;
BEGIN
SELECT *
Fetch all rows into BULK COLLECT INTO l_employees
collection FROM employees; bulkcoll.sql
sequentially, bulkcollect.tst

starting with 1. FOR indx IN 1 .. l_employees.COUNT


LOOP
process_employee (l_employees(indx));
Iterate through END LOOP;
the collection END;
contents with a
loop. But what if I need to fetch and process
millions of rows?
This approach could consume unacceptable
amounts of PGA memory.
Copyright 2013 Feuerstein and Associates Page 77
Oracle PL/SQL Programming

Limit rows returned by BULK COLLECT


CREATE OR REPLACE PROCEDURE bulk_with_limit
(deptno_in IN dept.deptno%TYPE)
IS
CURSOR emps_in_dept_cur IS
SELECT * FROM emp
WHERE deptno = deptno_in;

TYPE emp_tt IS TABLE OF emps_in_dept_cur%ROWTYPE;


emps emp_tt;
BEGIN
OPEN emps_in_dept_cur;
LOOP Use the LIMIT clause with the
FETCH emps_in_dept_cur INTO to manage the amount of
BULK COLLECT INTO emps LIMIT 1000; memory used with the BULK
COLLECT operation.
EXIT WHEN emps.COUNT = 0;
Definitely the preferred approach
process_emps (emps); in production applications with
END LOOP; large or varying datasets.
CLOSE emps_in_dept_cur;
END bulk_with_limit;
bulklimit.sql
Copyright 2013 Feuerstein and Associates Page 78
Oracle PL/SQL Programming

Details on that LIMIT clause


The limit value can be a literal or a variable.
I suggest using passing the limit as a parameter to
give you maximum flexibility.
A limit of 100 seems like a good default value.
Setting it to 500 or 1000 doesn't seem to make
much difference in performance.
With very large volumes of data and small
numbers of batch processes, however, a larger
LIMIT could help.

Copyright 2013 Feuerstein and Associates Page 79


Oracle PL/SQL Programming

Anti-Pattern Exercise Time!

PL/SQL Challenge - Play a Quiz

Anti-Pattern PL/SQL -
BULK COLLECT

Copyright 2013 Feuerstein and Associates Page 80


Oracle PL/SQL Programming

BULK COLLECT Conclusions


BULK COLLECT improves performance of queries
that retrieve more than one row.
Use the LIMIT clause to avoid excessive PGA
memory consumption.
On 10.1 and higher, the optimizer automatically
optimizes cursor FOR loops to run at performance
levels similar to BULK COLLECT.
So leave your cursor for loops in place if they
contain no DML operations or seem to be running
fast enough.
Explicit BULK COLLECTs will usually run a little faster
than cursor for loops optimized to BC.

Copyright 2013 Feuerstein and Associates Page 81


Oracle PL/SQL Programming

Use FORALL for repeated DML operations


PROCEDURE upd_for_dept (...) IS
BEGIN
FORALL indx IN low_value .. high_value
UPDATE employee
SET salary = newsal_in
WHERE employee_id = list_of_emps (indx);
END; Bind array

Convert loops that contain inserts, updates,


deletes or merges to FORALL statements.
Header looks identical to a numeric FOR loop.
Implicitly declared integer iterator
At least one "bind array" that uses this iterator as its
index value.
You can also use a different header "style" with INDICES
OF and VALUES OF (covered later)
forall_timing.sql
Copyright 2013 Feuerstein and Associates forall_examples.sql Page 82
Oracle PL/SQL Programming

More on FORALL
Use any type of collection with FORALL.
Only one DML statement is allowed per
FORALL.
Each FORALL is its own "extended" DML
statement.
The collection must be indexed by integer.
The bind array must be sequentially filled.
Unless you use the INDICES OF or
VALUES OF clause.
Indexes cannot be expressions. forall_restrictions.sql

Copyright 2013 Feuerstein and Associates Page 83


Oracle PL/SQL Programming

How many rows were modified?


SQL%ROWCOUNT returns total number of
rows modified by entire FORALL.
Not to be relied on when used with LOG
ERRORS.
Use the SQL%BULK_ROWCOUNT cursor
attribute to determine how many rows are
modified by each statement.
A "pseudo-collection" of integers; no methods
are defined for this element.

bulk_rowcount.sql

Copyright 2013 Feuerstein and Associates Page 84


Oracle PL/SQL Programming

Anti-Pattern Exercise Time!

PL/SQL Challenge - Play a Quiz

Anti-Pattern PL/SQL -
FORALL Basics

Copyright 2013 Feuerstein and Associates Page 85


Oracle PL/SQL Programming

Using FORALL with Sparse Collections


Prior to Oracle10g R2, the binding arrays in a
FORALL statement must be sequentially filled.
Now, however, you can bind sparse collections
by using INDICES OF and VALUES OF in the
FORALL header.
PROCEDURE upd_for_dept (...) IS
BEGIN
FORALL indx IN INDICES OF list_of_emps
UPDATE employee
SET salary = newsal_in
WHERE employee_id = list_of_emps (indx);
10g_indices_of*.sql
10g_values_of*.sql

Copyright 2013 Feuerstein and Associates Page 86


Oracle PL/SQL Programming

FORALL and DML Errors


FORALLs typically execute a large number of DML
statements.
When an exception occurs in one of those DML
statement, the default behavior is:
That statement is rolled back and the FORALL stops.
All (previous) successful statements are not rolled
back.
What if you want the FORALL processing to continue,
even if an error occurs in one of the statements?
Just add the SAVE EXCEPTIONS clause and then check
the SQL%BULK_EXCEPTIONS array.

Copyright 2013 Feuerstein and Associates Page 87


Oracle PL/SQL Programming

Using SAVE EXCEPTIONS


For each exception raised, Oracle populates the
SQL%BULK_EXCEPTIONS pseudo-collection of
records, with two fields.
ERROR_INDEX: the number of the DML statement that
failed (sequentially generated).
ERROR_CODE: the number (positive) for the error that was
raised
Unfortunately, it does not store the error message.
When the FORALL statement completes, if at least
one exception occurred, Oracle then raises ORA-
24381.
bulkexc.sql
bulkexc_indices_of*.sql
Copyright 2013 Feuerstein and Associates Page 88
Oracle PL/SQL Programming

FORALL with SAVE EXCEPTIONS


Add SAVE EXCEPTIONS to enable FORALL to
suppress errors at the statement level.
If any exception is
encountered,
CREATE OR REPLACE PROCEDURE load_books (
books_in IN book_obj_list_t) Oracle raises -
IS 24381 when done.
bulk_errors EXCEPTION;
PRAGMA EXCEPTION_INIT ( bulk_errors, -24381 );
BEGIN
FORALL indx IN books_in.FIRST..books_in.LAST Allows processing of all
SAVE EXCEPTIONS statements, even after
INSERT INTO book values (books_in(indx)); an error occurs.
EXCEPTION
WHEN bulk_errors THEN
FOR indx in 1..SQL%BULK_EXCEPTIONS.COUNT Iterate through
LOOP "pseudo-collection"
log_error (SQL%BULK_EXCEPTIONS(indx).ERROR_INDEX of errors.
, SQL%BULK_EXCEPTIONS(indx).ERROR_CODE);
END LOOP;
END;
bulkexc.sql
bulkexc.sql
bulkexc_indices_of*.sql
Copyright 2013 Feuerstein and Associates Page 89
Oracle PL/SQL Programming

Converting to Bulk Processing


Let's take a look at the process by which you
go from "old-fashioned" code to a bulk
processing-based solution.
From integrated row-by-row to phased
processing
With multiple DML statements in loop, how
do you "communicate" from one to the other?

Copyright 2013 Feuerstein and Associates Page 90


Oracle PL/SQL Programming

The "Old Fashioned" Approach


Cursor FOR loop with two DML statements, trap
exception, and keep on going.
CREATE OR REPLACE PROCEDURE upd_for_dept (
dept_in IN employees.department_id%TYPE
, newsal_in IN employees.salary%TYPE)
IS
CURSOR emp_cur ...;
BEGIN
FOR rec IN emp_cur
LOOP
BEGIN
INSERT INTO employee_history ...

adjust_compensation (rec.employee_id, rec.salary);

UPDATE employees SET salary = rec.salary ...


EXCEPTION
WHEN OTHERS THEN log_error;
END;
END LOOP;
END upd_for_dept;
cfl_to_bulk_0.sql
Copyright 2013 Feuerstein and Associates Page 91
Oracle PL/SQL Programming

A phased approach with bulk processing


Change from integrated, row-by-row approach to
a phased approach.
Phase 1: Bulk collect from table(s) to collection
Relational
Table

Phase 2: Modify contents of collection


according to requirements

Relational
Table
Phase 3: FORALL from collection to table
Copyright 2013 Feuerstein and Associates Page 92
Oracle PL/SQL Programming

Translating phases into code


The cfl_to_bulk_5.sql file contains the
converted program, following the phased
approach.
BEGIN
Phase 1: OPEN employees_cur;
Get Data
LOOP
Phase 3: fetch_next_set_of_rows (
Push Data bulk_limit_in, employee_ids, salaries, hire_dates);
EXIT WHEN employee_ids.COUNT = 0;
Phase 2: insert_history;
Massage Data adj_comp_for_arrays (employee_ids, salaries);
update_employee;
END LOOP;
Phase 3:
END upd_for_dept;
Push Data

cfl_to_bulk_0.sql
Copyright 2013 Feuerstein and Associates cfl_to_bulk_5.sql Page 93
Oracle PL/SQL Programming

Anti-Pattern Exercise Time!

PL/SQL Challenge - Play a Quiz

Anti-Pattern PL/SQL -
FORALL Advanced

Copyright 2013 Feuerstein and Associates Page 94


Oracle PL/SQL Programming

Conclusions Bulk Processing


FORALL is the most important performance tuning
feature in PL/SQL.
Almost always the fastest way to execute repeated SQL
operations in PL/SQL.
You trade off increased complexity of code for
dramatically faster execution.
But remember that Oracle will automatically optimize
cursor FOR loops to BULK COLLECT efficiency.
No need to convert unless the loop contains DML or you
want to maximally optimize your code.
Watch out for the impact on PGA memory!

Copyright 2013 Feuerstein and Associates Page 95


Oracle PL/SQL Programming

Multiple Retrievals of Same Data


Final challenge: how can we avoid querying
the same (unchanged) data again and again?
Our applications are filled with the same or similar
SQL statements, often getting the same rows.

CREATE OR REPLACE PROCEDURE do_stuff_with_employee (


employee_id_in IN employees.employee_id%TYPE)
IS
l_employee employees%ROWTYPE;
BEGIN
SELECT *
INTO l_employee
FROM employees
WHERE employee_id = employee_id_in;
END;

Copyright 2013 Feuerstein and Associates Page 96


Oracle PL/SQL Programming

Caching is the Answer!


Store unchanged values in a cache, and then
retrieve data from that cache.
Options for PL/SQL Programmers include:
Materialized views (well, sort of)
PGA caching with packages
SGA caching with the function result cache

Copyright 2013 Feuerstein and Associates Page 97


Oracle PL/SQL Programming

PGA-Based Caching
When you declare variables at the package
level, their state persists in your session.
A PGA-based cache, specific to each session.
And if you declare a collection at the package
level, you can cache multiple rows of data.
Very useful for static datasets like materialized
views.
Not a reliable technique for Web-based
(usually stateless) applications

Copyright 2013 Feuerstein and Associates Page 98


Oracle PL/SQL Programming

Avoiding Unnecessary SGA Lookups


Data retrieved
First access Pass Data from cache Data returned
to Cache to application

Database Application
/ SGA Function
Not in cache; PGA
Request data
from database Application
Requests Data

Subsequent accesses Data retrieved Data returned


from cache to application

Data found in
Database cache. Database Application
/ SGA is not needed. Function
PGA
Application
Requests Data
emplu.pkg / emplu.tst
Copyright 2013 Feuerstein and Associates Page 99
Oracle PL/SQL Programming

PGA Caching: Things to keep in mind


Must use package-level data so that it persists.
Memory is consumed by the PGA and so is multiplied for all
users of the application.
Not a reliable technique for stateless application (Internet)
Very difficult to share cache across sessions in the same
instance.
One possibility involves DBMS_PIPE.
Very difficult to update the cache once the data source is
changed.
Especially by/from, other sessions. Possible to use DBMS_ALERT.
Useful under specific scenarios....
Small, static dataset
Single or small number of batch processes

syscache.pkg
Copyright 2013 Feuerstein and Associates Page 100
Oracle PL/SQL Programming

Anti-Pattern Exercise Time!

PL/SQL Challenge - Play a Quiz

Anti-Pattern PL/SQL -
PGA Caching with Packages

Copyright 2013 Feuerstein and Associates Page 101


11g Oracle PL/SQL Programming

The Oracle 11g Function Result Cache


11g offers a far superior caching solution
than PGA caching in 11g: the Function Result
Cache.
This cache is...
stored in the SGA
shared across sessions
purged of dirty data automatically
You can use and should use it to retrieve data
from any table that is queried more
frequently than updated.

Copyright 2013 Feuerstein and Associates Page 102


11g Oracle PL/SQL Programming

How the Function Result Cache Works


Add the RESULT_CACHE clause to your function's
header.
When a call is made to function, Oracle
compares IN argument values to the cache.
If no match, the function is executed and the
inputs and return data are cached.
If a match is found, the function is not executed;
cached data is returned.
If changes to a "relies on" table are committed,
the cache is marked invalid and will be re-built.
11g_frc_demo.sql
Copyright 2013 Feuerstein and Associates Page 103
11g Oracle PL/SQL Programming

Minimal Impact on Code with Result Cache


CREATE OR REPLACE PACKAGE emplu11g
IS
FUNCTION onerow (employee_id_in IN employees.employee_id%TYPE)
RETURN employees%ROWTYPE
RESULT_CACHE;
END emplu11g;

CREATE OR REPLACE PACKAGE BODY emplu11g


IS
FUNCTION onerow (employee_id_in IN employees.employee_id%TYPE)
RETURN employees%ROWTYPE
RESULT_CACHE RELIES_ON (employees)
IS
....
END onerow;
END emplu11g;

Add RESULT_CACHE keyword to header of function in both


specification and body.
RELIES_ON clause is deprecated in 11.2. Oracle will
automatically determine all tables on which the function
relies. RELIES_ON is then ignored.
Copyright 2013 Feuerstein and Associates Page 104
11g Oracle PL/SQL Programming

Performance Impact of Result Cache


The result cache is stored in the SGA.
So we should expect it be slower than a PGA-
based cache.
But accessing result cache data does not
require going through the SQL engine.
So it should be much faster than executing a
query.
Even if the statement is parsed and the data
blocks are already in the SGA.
Let's find out!

11g_emplu*.*
Copyright 2013 Feuerstein and Associates Page 105
11g Oracle PL/SQL Programming

Result Cache Things to Keep in Mind


If you have uncommitted changes in your
session, dependent caches are ignored.
The cache will not override your own changed data.
Caching is not performed for complex types:
records with CLOBs, collections, etc.
But Oracle is optimistic!
The cache is not related to SQL statements in
your function.
It only keeps track of the input values and the
RETURN clause data.
11g_frc_demo.sql
Copyright 2013 Feuerstein and Associates Page 106
Oracle PL/SQL Programming

Anti-Pattern Exercise Time!

PL/SQL Challenge - Play a Quiz

Anti-Pattern PL/SQL -
Function Result Cache Basics

Copyright 2013 Feuerstein and Associates Page 107


11g Oracle PL/SQL Programming

Result Cache and Session Dependencies


Functions with session-specific dependencies
must be "result-cached" with great care.
References to SYSDATE, reliance on
NLS_DATE_FORMAT, time zone changes
Application contexts (calls to SYS_CONTEXT)
Virtual private database configurations
Solution: move dependencies into parameter
list (the "unique index" for the cache).

11g_frc_session_state.sql
11g_frc_vpd.sql
Copyright 2013 Feuerstein and Associates 11g_frc_vpd2.sql Page 108
11g Oracle PL/SQL Programming

Managing the Result Cache


Oracle offers a number of ways to manage the
result cache and tune it to your specific
application needs:
RESULT_CACHE_SIZE initialization parameter
If the cache is too small, then the LRU algorithm
negates the point of the cache.
DBMS_RESULT_CACHE management package
v$RESULT_CACHE_* performance views

show_frc_dependencies.sp
Copyright 2013 Feuerstein and Associates Page 109
11g Oracle PL/SQL Programming

Fine Grained Dependencies in 11.2


Oracle keeps track of table dependencies on a
per-result level.
Each result cached could have a different set of
dependencies.
A change to a table could invalidate just a
subset of the results in the cache.
It's not all or nothing - when your function's
different logic paths could "hit" different tables.

11g_frc_dependencies.sql
Copyright 2013 Feuerstein and Associates
11g_frc_dependencies2.sql Page 110
Oracle PL/SQL Programming

Anti-Pattern Exercise Time!

PL/SQL Challenge - Play a Quiz

Anti-Pattern PL/SQL -
Advanced Function Result Cache
Topics

Copyright 2013 Feuerstein and Associates Page 111


Oracle PL/SQL Programming

Make It Easy on Yourself


I hope you will agree that the result cache is a
great feature.
But how easy will it be for you to apply it?
If you write/duplicate queries throughout your
code, upgrading will be expensive and slow.
If you hide your queries behind functions, you
have a single point of definition, and you can
upgrade "instantly."

11g_frc_encapsulation.sql
Copyright 2013 Feuerstein and Associates Page 112
Oracle PL/SQL Programming

The Power of Names

Copyright 2013 Feuerstein and Associates Page 113


Oracle PL/SQL Programming

What's In a Name?
A name is an abstraction, a symbol that is
associated with an underlying set of data.
A common concept in history is: knowing the
"true name" of something or someone gives
one power over that thing.
Le Guin's Earthsea cycle a classic example
Let's not forget about Rumplestiltskin.
And if you know God's true namewow!
Names exist in a hierarchy of abstraction.
Human -> Woman -> Mother -> Joan Feuerstein

Copyright 2013 Feuerstein and Associates Page 114


Oracle PL/SQL Programming

What's in a brain?
Humans constantly seek models to describe how
the brain works.
Most have failed dismally. Where art thou, AI?
Most would agree that the brain identifies and stores
data, which can be referenced by name.
Seems fairly clear that our brain is a big pattern
analysis engine, sorting through all the data our
senses provide.
Build plans of action based on those patterns.
One interesting consequence: stereotyping/prejudice
unavoidable feature of "being human"?
Check out: numenta.org and On Intelligence.
Copyright 2013 Feuerstein and Associates Page 115
Oracle PL/SQL Programming

Pattern Analysis and Abstraction

Or to bring it back to our favorite species.


Everyone
knows you're
a person
Copyright 2013 Feuerstein and Associates Page 116
Oracle PL/SQL Programming

Names in Software
Names are crucial to human efforts to
transform our world to make it more
convenient and more comfortable.
But what do they do for software?
Names are used in software in two key ways.
1. Hide information: avoid information overload!
2. Improve maintainability: make it easier to read
and understand code.

Copyright 2013 Feuerstein and Associates Page 117


Oracle PL/SQL Programming

Hide Information
Information hiding: a key principle of software
generally and object orientation in particular.
Human brains can handle only so much data.
We live in an age of information overload
(internet, 24 hour TV, etc.).
This is certainly true of complex software.
The closest humans come to "world creators."
So we hide details that are not needed at this
moment behind a name.

Copyright 2013 Feuerstein and Associates Page 118


Oracle PL/SQL Programming

Improve Maintainability of Code


Arguably the most important (and most
neglected) aspect of software.
Factors in maintainability include:
Self-documentation: comments are bad. :-)
Single point of definition: repetition is bad.
Ease (and associated low cost) of change over
time: spaghetti code is bad.
Let's take a look at features of PL/SQL that
relate to names.

Copyright 2013 Feuerstein and Associates Page 119


Oracle PL/SQL Programming

Where Names Are Found in PL/SQL


Where are they not found?
Software without names is, well, blobby.
Tables, views, columns
Program units and their subprograms
Typesand subtypes
Variables, constants and cursors
Labels for "unnamed" sections of code

Copyright 2013 Feuerstein and Associates Page 120


Oracle PL/SQL Programming

Guidelines for An Excellent PL/SQL Name


Has no more than 30 characters. Bummer!
And finds a nice balance between length and clarity
Does not rely on CamelNotation.
Don't fight case insensitivity of SQL-PL/SQL!
Reflects the information hidden behind the
name.
A misleading name makes reading code extremely
difficult.
Standardize the structure of the name.
Prefixes, suffixes, singular vs. plural

get_the_name_right.sql
Copyright 2013 Feuerstein and Associates Page 121
Oracle PL/SQL Programming

Program Units and Names


The starting point (leaving aside
tables and views) for all PL/SQL
developers.
Start with package names, then
subprogram names, then
nested subprogram names.
Package names: define an area of
functionality, into which are
collected multiple procedures
and functions.
Subprogram names: procs do
things, functions return things.
Don't put "get" in function name!

Copyright 2013 Feuerstein and Associates Page 122


Oracle PL/SQL Programming

Nested Subprograms "extreme" modularization -> high readability

You can declare a procedure or


function in any declaration
section.
An anonymous block
Another procedure or function
Nested subprograms offer "table of contents" for subprogram

tremendous power
to improve readability
through information
hiding.

Copyright 2013 Feuerstein and Associates Page 123


Oracle PL/SQL Programming

Types and Subtypes


Oracle defines lots of "generic" types for us to
use (NUMBER, VARCHAR2,
DBMS_SQL.NUMBER_TABLE, etc.).
We can also create our own types, as well as
the simple but powerful SUBTYPE.
Declare types in package specs so they can be re-
used easily.
Subtypes give application-specific names for
commonly-used declaration types.

plsql_limits.pks
string_tracker3.*
Copyright 2013 Feuerstein and Associates Page 124
Oracle PL/SQL Programming

About SUBTYPEs
You can't always use %TYPE or %ROWTYPE in your
declaration.
You can, however, always define a "subtype" or
subset of an existing type with the SUBTYPE
statement. SUBTYPE benefits:
Avoid exposing and repeating constraints.
Give application-specific names to types. Critical when
working with complex structures like collections of
records, and nested collections.
Apply constraints, such as numeric ranges, to the variable
declared with the subtype.

Copyright 2013 Feuerstein and Associates Page 125


Oracle PL/SQL Programming

Applying SUBTYPEs
Two key scenarios:
Whenever you are about to write a VARCHAR2(N)
or other constrained declaration, define a subtype
instead, preferably in a package specification.
Instead of writing a comment explaining a
declaration, put the explanation into a subtype.

DECLARE
Instead l_full_name VARCHAR2(100);
of this: l_big_string VARCHAR2(32767);

DECLARE
Write
l_full_name employees_rp.full_name_t;
this: l_big_string plsql_limits.maxvarchar2; fullname.pks
plsql_limits.pks
string_tracker3.*
Copyright 2013 Feuerstein and Associates Page 126
Oracle PL/SQL Programming

Variables, Constants and Cursors


As with program units, make sure your names
reflect the content/usage of your variable.
Use constants to hide literal values, providing
a single point of definition.
You can also declare a cursor in a package
specification (a named query, in essence).
But watch out! The cursor will persist in your
session - you must close it explicitly when done.

hardcoding.sql
no_more_hardcoding.sql
pkgcur.pkg
Copyright 2013 Feuerstein and Associates Page 127
Oracle PL/SQL Programming

Labels for "Unnamed" Code Sections


Nested blocks are "anonymous".
Loops are "anonymous."
But you can give them names with labels!
But I recommend that you make them nested
subprograms instead.
Use Toad's Refactoring tool to make sure you "do it right."
BEGIN
<<per_month_report>>
FOR indx IN 1 .. 12 LOOP
...
END LOOP per_month_report;

<<close_orders>>
DECLARE
l_new_local_var NUMBER := 100;
BEGIN
...
END close_orders;

Copyright 2013 Feuerstein and Associates Page 128


Oracle PL/SQL Programming

The Power of Hiding


Repeat after me: Everything is going to
change.
When you hide the "mechanics", how you get
things done, behind a procedure or function,
you are "liberated."
Change the implementation, and you don't need
to change all the places in which it is used.
Back to that same principle:
Never Repeat Anything.
Aim for Single Point of Definition.
Copyright 2013 Feuerstein and Associates Page 129
Oracle PL/SQL Programming

Anti-Pattern Exercise Time!

PL/SQL Challenge - Play a Quiz

Anti-Pattern PL/SQL -
Power of Names

Copyright 2013 Feuerstein and Associates Page 130


Oracle PL/SQL Programming

The Power of Names


Names contain an enormous amount of
explanatory and manipulative power.
With great power, comes great responsibility.
Thanks, Spiderman!
So first maximize opportunities to replace
complexity with a name.
Next, choose your names with care.
The result? Sparkling clear software that will
be given the name: "Excellent Stuff"
Copyright 2013 Feuerstein and Associates Page 131
Oracle PL/SQL Programming

Oracle Database 12c PL/SQL Features


More PL/SQL-Only Data Types Cross PL/SQL-to-SQL
Interface
Optimizing Function Execution in SQL
The ACCESSIBLE_BY Clause
FETCH FIRST and BULK COLLECT
Privileges/Access Management for Program Units
Grant Roles to Program Units
And INHERIT PRIVILEGES and INHERIT ANY PRIVILEGES
BEQUEATH CURRENT_USER for Views
New Conditional Compilation Directives
Implicit Statement Results
Goodbye, Tiny SQL VARCHAR2!

Copyright 2013 Feuerstein and Associates Page 132


Oracle PL/SQL Programming

More PL/SQL-Only Data Types Cross


PL/SQL-to-SQL Interface
Prior to 12c, PL/SQL-only datatypes could not
be bound in dynamic SQL statements,
restricted what functions could be called in
SQL, etc.
Now, those rules are greatly relaxed.
Bind records and associative arrays
Use TABLE operator with associative arrays
Can bind Booleans with dynamic PL/SQL, but you
cannot bind Booleans into SQL statements.
12c_table*.sql
Copyright 2013 Feuerstein and Associates
12c_bind*.sql Page 133
Oracle PL/SQL Programming

Optimizing Function Execution in SQL


That seems like an awfully good idea!
Two methods:
WITH clause that defines a function
UDF pragma that gives more options to the
compiler
WITH FUNCTION: define a function directly
within your SQL statement.
Say goodbye to nasty context switch!

12c_with_function*.sql
Copyright 2013 Feuerstein and Associates 12c_udf.sql & 12c_udf2.sql Page 134
Oracle PL/SQL Programming

FETCH FIRST N for BULK COLLECT


BULK COLLECT now supports an optional
FETCH FIRST clause.
Limits the number of rows that a query returns,
reducing the complexity of common "Top-N"
queries.
FETCH FIRST is provided primarily to simplify
migration from third-party databases to
Oracle Database.

12c_fetch_first.sql
Copyright 2013 Feuerstein and Associates Page 135
Oracle PL/SQL Programming

The ACCESSIBLE_BY Clause


ACCESSIBLE_BY extends the concept of
"privacy" for package subprograms.
Use it to define a "whitelist" of program units
that can invoke a package's subprograms.
PACKAGE BODY public_pkg
PACKAGE private_pkg
IS
ACCESSIBLE BY (public_pkg)
PROCEDURE do_only_this
IS
IS
PROCEDURE do_this;
BEGIN
private_pkg.do_this;
PROCEDURE do_that;
private_pkg.do_that;
END;
END;
END;

12c_accessible_by.sql
Copyright 2013 Feuerstein and Associates Page 136
Oracle PL/SQL Programming

Privilege-Related Features
First, let's review definer and invoker rights.
Definer Rights
Whenever you executed a stored program, it runs
under the privileges of the schema in which the
program was compiled or defined.
Invoker Rights
Oracle resolves all data references (table, view,
etc.) at run-time, based on the currently-connect
user and its privileges (directly granted or available
through roles).

Copyright 2013 Feuerstein and Associates Page 137


Oracle PL/SQL Programming

About Definer Rights


OE Code
Allows you to Sam_Sales
Order_Mgt
centralize access to
Place
and control of Close Old
Orders
Cancel
underlying data
structures.
Ignores roles and relies OE Data X Cannot alter
on directly-granted table
privileges. Orders directly.
But it can be a source
of confusion and Note: Oracle built-in packages
architectural problems. have long had the capability of
running under the invoker's
authority.

Copyright 2013 Feuerstein and Associates Page 138


Oracle PL/SQL Programming

Problems with Definer Rights


Deployment & maintenance
Must install module in all remote databases where needed
In some databases, each user has own copy of table(s),
requiring copy of stored module
Security
No declarative way to restrict privileges on certain modules
in a package -- it's all or nothing, unless you write code in the
package to essentially recreate roles programmatically.
Difficult to audit privileges
Sure would be nice to have a choice...and now you do!

Copyright 2013 Feuerstein and Associates Page 139


Oracle PL/SQL Programming

"Reflection" Capability of Invoker Rights


With invoker rights, you can execute code owned by
another schema, yet have all references to data
structures "reflect back" into your own schema.

Central Code schema


User/Data schema
PACKAGE acct_mgr PROCEDURE mng_account IS
make BEGIN
AUTHID modify ...
code.acct_mgr.destroy(...);
CURRENT_USER destroy END;

...FROM accounts accounts table


WHERE...

Copyright 2013 Feuerstein and Associates Page 140


Oracle PL/SQL Programming

Grant Roles to Program Units (12.1)


You can now grant roles to program units, so you fine-
tune the privileges available to the invoker of a
program unit.
Helpful when you don't want the invoker to inherit all of
the definer's privileges.
Roles granted to a program unit do not affect
compilation.
Instead, they affect the privilege checking of SQL
statements that the unit issues at run time.
So the program unit executes with the privileges of both its
own roles and any other currently enabled roles.
Most helpful when the program unit executes dynamic
SQL (no privilege checking till runtime).
invdefinv.sql
12c_grant_roles_units.sql
Copyright 2013 Feuerstein and Associates 12c_roles_for_program_units.sql Page 141
Oracle PL/SQL Programming

BEQUEATH CURRENT_USER for Views


Prior to 12.1, if your view executed a function,
it would always be run under the privileges of
the view's owner, and not that of the function.
Even if the function was defined with AUTHID
CURRENT_USER
Add the BEQUEATH CURRENT_USER clause
and then the invoker right's mode of the
function will be "honored."

12c_bequeath.sql
Copyright 2013 Feuerstein and Associates Page 142
Oracle PL/SQL Programming

INHERIT PRIVILEGES and INHERIT ANY


PRIVILEGES
More fine-tuning for privilege management!
You can override AUTHID and BEQUEATH
settings by revoking INHERIT PRIVILEGES.
On a schema-level basis
You can say, in effect: "All schemas but SCOTT
can use my privileges when running X."
After upgrade, all works as before.
INHERT PRIVILEGES granted to all schemas

12c_inherit_privileges.sql
Copyright 2013 Feuerstein and Associates Page 143
Oracle PL/SQL Programming

New Conditional Compilation Directives


Oracle has added two new directives.
$$PLSQL_UNIT_OWNER
Returns the name of the owner of the current
program unit. Returns NULL if an anonymous
block.
$$PLSQL_UNIT_TYPE
Returns the type of the program unit
Inside an anonymous block or non-DML trigger,
returns "ANONYMOUS BLOCK".

Copyright 2013 Feuerstein and Associates Page 144


Oracle PL/SQL Programming

Implicit Statement Results


I hate when a SQL Server developer says "Ha,
ha, I can do this and you can't."
Well, with 12.1, there's one less thing that
developer can talk about.
Not that there was ever all that much.
We can now create a procedure that will
implicitly display the result of a SQL
statement.
And it breathes some life back into DBMS_SQL!
12c_implicit_results.sql
Copyright 2013 Feuerstein and Associates Page 145
Oracle PL/SQL Programming

Goodbye, Tiny SQL VARCHAR2!


It sure has been irritating that PL/SQL
supports VARCHAR2s up to 32K in size (after
which , you must switch over to CLOBs), while
in SQL, the maximum was 4000.
Now, SQL's VARCHAR2 and NVARCHAR2 have
been extended to 32K as well!
Note: SQL has these maximum sizes only if the
MAX_STRING_SIZE initialization parameter is
set to EXTENDED.
12c_sql_varchar2.sql
Copyright 2013 Feuerstein and Associates Page 146
Oracle PL/SQL Programming

Oracle12c New PL/SQL Features


Nothing earth-shattering, but a solid
improvement on an already robust language.
Improved integration with SQL
Better support for migration from TransactSQL
and other database languages
Fine-tuning of privileges management and access
control.
My favorite by far: ability to use TABLE with
associative arrays.

Copyright 2013 Feuerstein and Associates Page 147


Oracle PL/SQL Programming

Make the Most of Oracle PL/SQL!


This language is not evolving very rapidly
these days (less change than in SQL).
Make sure that you are aware of key new (and
existing) features, and put them to use.
Always prioritize the maintainability of your
code.
It's going to be around for YEARS to come!

Copyright 2013 Feuerstein and Associates Page 148

Das könnte Ihnen auch gefallen