You are on page 1of 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 reorder 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
PL/SQL block
FOR rec IN emp_cur LOOP
UPDATE employee
SET salary = ...
WHERE employee_id =
rec.employee_id;
END LOOP;

Procedural
statement
executor

SQL Engine

SQL
statement
executor

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
last_in
RETURN VARCHAR2 IS
BEGIN
RETURN first_in || ' ' || last_in;
END;
/

IN VARCHAR2,
IN VARCHAR2)

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

Library cache
Shared SQL

Select *
from emp

Pre-parsed

Large Pool

Session 1

calc_totals

show_emps

Update emp
Set sal=...

upd_salaries

emp_rec emp%rowtype;
tot_tab tottabtype;

emp_rec emp%rowtype;
tot_tab tottabtype;

Session 1 memory
(PGA/UGA)

Session 2 memory
(PGA/UGA)

Copyright 2013 Feuerstein and Associates

Session 2

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
plsql_memory.start_analysis;
run_my_application;
plsql_memory.show_memory_usage;
END;
Copyright 2013 Feuerstein and Associates

show_pga_uga.sql
grantv$.sql
plsql_memory.pkg
plsql_memory_demo.sql

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.

Use pipelined table functions.


Copyright 2013 Feuerstein and Associates

bulklimit.sql
varray_collection_limit.sql
nocopy*.tst
tabfunc_pipelined.sql
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.
Copyright 2013 Feuerstein and Associates

raise.sql
raise_application_error.sql

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.
Copyright 2013 Feuerstein and Associates

info_for_handling.sql
excquiz*.sql

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
Copyright 2013 Feuerstein and Associates

errors_and_dml.sql
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 builtins 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
lists of information, similar to 3GL
arrays.
Use them to maintain in-program
(PGA) listsvery fast!
Collections enable other key features
of PL/SQL

Apple

22

Pear

100

Orange

10023

Apricot

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.
Copyright 2013 Feuerstein and Associates

method_vs_proc.sql
plsqlloops.sp

collection_exists.sql
varray_limit.sql
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 sequencegenerated 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.

Copyright 2013 Feuerstein and Associates

assoc_array*.sql
assoc_array_perf.tst
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

Copyright 2013 Feuerstein and Associates

List 2: 10001-20000

List 2: 20001-30000

string_tracker3*.*

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;
Copyright 2000-2008 Steven Feuerstein - Page 51
Copyright 2013 Feuerstein and Associates

authors.pkg
10g_set.sql

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.
Copyright 2013 Feuerstein and Associates

in_clause.*
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;
/

Copyright 2013 Feuerstein and Associates

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

Copyright 2013 Feuerstein and Associates

authors.pkg
10g_union.sql

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.

Copyright 2013 Feuerstein and Associates

10g_intersect.sql

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.
Copyright 2013 Feuerstein and Associates

10g_except.sql

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
Adds a subquery to a query like a function call.

Copyright 2013 Feuerstein and Associates

select d.deptno
, (select count(*)
from emp e where
e.deptno = d.deptno)
number_staff from dept
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
FROM
WHERE
END;

*
l_manager
employees
employee_id = l_employee.manager_id;

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
PL/SQL block
FORALL indx IN
list_of_emps.FIRST..
list_of_emps.LAST
UPDATE employee
SET salary = ...
WHERE employee_id =
list_of_emps(indx);
Update...
Update...
Update...
Update...
Update...
Update...

Copyright 2013 Feuerstein and Associates

Procedural
statement
executor

Fewer context switches,


same SQL behavior

SQL Engine

SQL
statement
executor
Update...
Update...
Update...
Update...
Update...
Update...

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 statementlevel triggers only fire once per FORALL INSERT
statements.
Not for each INSERT statement passed to the SQL engine
from the FORALL statement.
Copyright 2013 Feuerstein and Associates

statement_trigger_and_forall.sql

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 a nested
table of records to
hold the queried
data.
Fetch all rows into
collection
sequentially,
starting with 1.

Iterate through
the collection
contents with a
loop.

Copyright 2013 Feuerstein and Associates

DECLARE
TYPE employees_aat IS TABLE OF
employees%ROWTYPE;
l_employees employees_aat;
BEGIN
SELECT *
BULK COLLECT INTO l_employees
FROM employees;

bulkcoll.sql
bulkcollect.tst

FOR indx IN 1 .. l_employees.COUNT


LOOP
process_employee (l_employees(indx));
END LOOP;
END;

But what if I need to fetch and process


millions of rows?
This approach could consume unacceptable
amounts of PGA memory.
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


in production applications with
large or varying datasets.

process_emps (emps);
END LOOP;
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)
Copyright 2013 Feuerstein and Associates

forall_timing.sql
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 ORA24381.
Copyright 2013 Feuerstein and Associates

bulkexc.sql
bulkexc_indices_of*.sql

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,
Oracle raises 24381 when done.

CREATE OR REPLACE PROCEDURE load_books (


books_in IN book_obj_list_t)
IS
bulk_errors EXCEPTION;
PRAGMA EXCEPTION_INIT ( bulk_errors, -24381 );
BEGIN
Allows processing of all
FORALL indx IN books_in.FIRST..books_in.LAST
statements, even after
SAVE EXCEPTIONS
an error occurs.
INSERT INTO book values (books_in(indx));
EXCEPTION
WHEN bulk_errors THEN
Iterate through
FOR indx in 1..SQL%BULK_EXCEPTIONS.COUNT
"pseudo-collection"
LOOP
log_error (SQL%BULK_EXCEPTIONS(indx).ERROR_INDEX
of errors.
, SQL%BULK_EXCEPTIONS(indx).ERROR_CODE);
END LOOP;
END;
Copyright 2013 Feuerstein and Associates

bulkexc.sql
bulkexc.sql
bulkexc_indices_of*.sql

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

Phase 1: Bulk collect from table(s) to collection

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.
Phase 1:
Get Data
Phase 3:
Push Data
Phase 2:
Massage Data

Phase 3:
Push Data

BEGIN
OPEN employees_cur;
LOOP
fetch_next_set_of_rows (
bulk_limit_in, employee_ids, salaries, hire_dates);
EXIT WHEN employee_ids.COUNT = 0;
insert_history;
adj_comp_for_arrays (employee_ids, salaries);
update_employee;
END LOOP;
END upd_for_dept;

Copyright 2013 Feuerstein and Associates

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


First access

Data retrieved
from cache

Pass Data
to Cache

Database
/ SGA

Application

Function
Not in cache;
Request data
from database

PGA
Application
Requests Data

Subsequent accesses
Database
/ SGA

Data returned
to application

Data retrieved
from cache

Data found in
cache. Database
is not needed.

Data returned
to application

Application

Function

PGA
Application
Requests Data
Copyright 2013 Feuerstein and Associates

emplu.pkg / emplu.tst

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
Copyright 2013 Feuerstein and Associates

syscache.pkg

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.
Copyright 2013 Feuerstein and Associates

11g_frc_demo.sql

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


Copyright 2013 Feuerstein and Associates

11g_emplu*.*

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.
Copyright 2013 Feuerstein and Associates

11g_frc_demo.sql

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

Copyright 2013 Feuerstein and Associates

11g_frc_session_state.sql
11g_frc_vpd.sql
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

Copyright 2013 Feuerstein and Associates

show_frc_dependencies.sp

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.

Copyright 2013 Feuerstein and Associates

11g_frc_dependencies.sql
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."

Copyright 2013 Feuerstein and Associates

11g_frc_encapsulation.sql

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


tremendous power
to improve readability
through information
hiding.

Copyright 2013 Feuerstein and Associates

"table of contents" for subprogram

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 reused easily.
Subtypes give application-specific names for
commonly-used declaration types.

Copyright 2013 Feuerstein and Associates

plsql_limits.pks
string_tracker3.*

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.
Instead
of this:
Write
this:

DECLARE
l_full_name VARCHAR2(100);
l_big_string VARCHAR2(32767);

DECLARE
l_full_name employees_rp.full_name_t;
l_big_string plsql_limits.maxvarchar2;

Copyright 2013 Feuerstein and Associates

fullname.pks
plsql_limits.pks
string_tracker3.*
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.

Copyright 2013 Feuerstein and Associates

hardcoding.sql
no_more_hardcoding.sql
pkgcur.pkg

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.
Copyright 2013 Feuerstein and Associates

12c_table*.sql
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!

Copyright 2013 Feuerstein and Associates

12c_with_function*.sql
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
IS
PROCEDURE do_only_this
IS
BEGIN
private_pkg.do_this;
private_pkg.do_that;
END;
END;

PACKAGE private_pkg
ACCESSIBLE BY (public_pkg)
IS
PROCEDURE do_this;
PROCEDURE do_that;
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


Allows you to
centralize access to
and control of
underlying data
structures.
Ignores roles and relies
on directly-granted
privileges.
But it can be a source
of confusion and
architectural problems.

Copyright 2013 Feuerstein and Associates

OE Code

Sam_Sales

Order_Mgt
Place

Close Old
Orders

Cancel

OE Data

Orders

Cannot alter
table
directly.

Note: Oracle built-in packages


have long had the capability of
running under the invoker's
authority.
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
PACKAGE acct_mgr
make
AUTHID
CURRENT_USER

...FROM accounts
WHERE...

Copyright 2013 Feuerstein and Associates

modify
destroy

User/Data schema
PROCEDURE mng_account IS
BEGIN
...
code.acct_mgr.destroy(...);
END;

accounts table

Page 140

Oracle PL/SQL Programming

Grant Roles to Program Units (12.1)


You can now grant roles to program units, so you finetune 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).

Copyright 2013 Feuerstein and Associates

invdefinv.sql
12c_grant_roles_units.sql
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
Copyright 2013 Feuerstein and Associates

12c_inherit_privileges.sql

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

You might also like