High
Performance
PL/SQL
Steven
Feuerstein
Architect
Oracle
CorporaGon
February
2015
Please
Stand
By.
This
session
will
begin
promptly
at
the
8me
indicated
on
the
agenda.
Thank
You.
Copyright
2014
Oracle
and/or
its
aliates.
All
rights
reserved.
|
Safe
Harbor
Statement
The
following
is
intended
to
outline
our
general
product
direcGon.
It
is
intended
for
informaGon
purposes
only,
and
may
not
be
incorporated
into
any
contract.
It
is
not
a
commitment
to
deliver
any
material,
code,
or
funcGonality,
and
should
not
be
relied
upon
in
making
purchasing
decisions.
The
development,
release,
and
Gming
of
any
features
or
funcGonality
described
for
Oracles
products
remains
at
the
sole
discreGon
of
Oracle.
Copyright
2014
Oracle
and/or
its
aliates.
All
rights
reserved.
|
High
Performance
PL/SQL
Agenda
The
funcGon
result
cache
BULK
COLLECT
and
FORALL
NOCOPY
hint
The
UDF
pragma,
new
to
Oracle
Database
12c
All
referenced
code
is
available
in
my
demo.zip
le
from
the
PL/SQL
Learning
Library:
oracle.com/oll/plsql.
Copyright
2014
Oracle
and/or
its
aliates.
All
rights
reserved.
|
PL/SQL
Run8me
Memory
Architecture
System Global Area (SGA) of RDBMS Instance
Shared
Pool
Reserved
Pool
Large
Pool
Session
1
emp_rec emp%rowtype;
tot_tab pkg.tottabtype;
Session
1
memory
UGA
User
Global
Area
PGA
Process
Global
Area
Library
cache
Shared
SQL
Pre-parsed
calc_totals
Select *
from emp
Update emp
Set sal=...
show_emps
upd_salaries
emp_rec emp%rowtype;
tot_tab pkg.tottabtype;
Session
2
memory
UGA
User
Global
Area
PGA
Process
Global
Area
Copyright
2014
Oracle
and/or
its
aliates.
All
rights
reserved.
|
Session
2
Data
Caching
Op8ons
in
PL/SQL
Caching
is
a
Gme-honored
technique
for
improving
performance.
Store
data
that
doesnt
change
for
some
period
of
+me
in
a
locaGon
that
can
be
accessed
more
quickly
than
the
source.
The
SGA
is
an
enormous,
complex
cache
for
an
enGre
database
instance.
DeterminisGc
funcGons
Under
some
circumstances,
can
avoid
execuGon
of
funcGon
with
matching
inputs
PGA
caching
using
package-level
variables
Very
fast,
but
per-session
cache,
only
for
staGc
datasets,
inapplicable
to
stateless
environments
like
the
Internet.
The
funcGon
result
cache
The
most
powerful
and
widely
applicable
caching
technique
for
PL/SQL
funcGons
Copyright
2014
Oracle
and/or
its
aliates.
All
rights
reserved.
|
The
Func8on
Result
Cache
This
cache
is
stored
in
the
SGA,
rather
than
PGA.
It
is
shared
across
sessions
connected
to
instance,
so
it
scales
well.
Oracle
automaGcally
invalidates
any
caches
that
contain
possibly
dirty
data.
You
can
use
and
should
use
it
to
retrieve
data
from
any
table
that
is
queried
more
frequently
than
updated.
And
the
same
rows
are
being
fetched
repeatedly.
Available
in
Enterprise
EdiGon
only.
You
can
include
the
FRC
syntax
in
code
running
in
Standard
EdiGon;
the
feature
will
simply
be
ignored.
Copyright
2014
Oracle
and/or
its
aliates.
All
rights
reserved.
|
How
the
Func8on
Result
Cache
Works
Add
the
RESULT_CACHE
clause
to
your
funcGon's
header.
That's
it!
Truly
minimal
impact
on
code
if
you've
"hidden"
SELECTS
in
funcGon.
When
a
call
is
made
to
funcGon,
Oracle
compares
argument
values
(must
be
IN
only)
to
the
cache.
If
no
match,
the
funcGon
is
executed
and
the
inputs
and
return
data
are
cached.
If
a
match
is
found,
the
funcGon
is
not
executed;
cached
data
is
returned.
If
changes
to
a
"relies
on"
table
are
commiied,
the
cache
is
marked
invalid
and
will
be
re-built.
11g_frc_demo.sql
11g_frc_encapsulation.sql
Copyright
2014
Oracle
and/or
its
aliates.
All
rights
reserved.
|
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
execuGng
a
query.
Even
if
the
statement
is
parsed
and
the
data
blocks
are
already
in
the
SGA.
sf_timer.*
11g_emplu.pkg
11g_emplu_test.sql
Copyright
2014
Oracle
and/or
its
aliates.
All
rights
reserved.
|
Result
Cache
Things
to
Keep
in
Mind
-
1
If
you
have
uncommiied
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,
collecGons,
etc.
But
Oracle
is
opGmisGc!
The
cache
is
not
related
to
SQL
statements
in
your
funcGon.
It
only
keeps
track
of
the
input
values
and
the
RETURN
clause
data.
11g_frc_demo.sql
Copyright
2014
Oracle
and/or
its
aliates.
All
rights
reserved.
|
Result
Cache
Things
to
Keep
in
Mind
-
2
You
cannot
use
the
result
cache
with
invoker
rights
program
units
unGl
12.1.
Bypass
execuGon
of
funcGon
body,
Oracle
cannot
resolve
references
to
objects
-
the
whole
point
of
IR.
FuncGons
with
session-specic
dependencies
must
be
"result-cached"
with
great
care.
Virtual
private
database
conguraGons
References
to
SYSDATE,
reliance
on
NLS_DATE_FORMAT,
Gme
zone
changes
ApplicaGon
contexts
(calls
to
SYS_CONTEXT)
SoluGon:
move
all
dependencies
into
parameter
list.
Copyright
2014
Oracle
and/or
its
aliates.
All
rights
reserved.
|
11g_frc_vpd.sql
11g_frc_vpd2.sql
Managing
the
Result
Cache
Oracle
oers
a
number
of
ways
to
manage
the
result
cache
and
tune
it
to
your
specic
applicaGon
needs.
And
that
tuning
process
is
cri+cal!
RESULT_CACHE_MAX_SIZE
iniGalizaGon
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
2014
Oracle
and/or
its
aliates.
All
rights
reserved.
|
Look
for
opportuni8es
to
cache!
Always
look
for
opportuniGes
to
cache
data.
Are
there
tables
that
are
always
staGc
during
user
sessions?
Are
there
tables
that
are
queried
much
more
frequently
than
they
are
changed?
But
cache
with
care!
Caching
inappropriately
can
lead
to
delivery
of
dirty
data
to
your
users.
The
funcGon
result
cache
oers
the
most
declaraGve,
widest
impact
caching
technique
available
to
PL/SQL
developers.
Copyright
2014
Oracle
and/or
its
aliates.
All
rights
reserved.
|
Turbo-charge
SQL
with
Bulk
Processing
Improve
the
performance
of
repeated
SQL
operaGons
by
an
order
of
magnitude
with
bulk/array
processing
in
PL/SQL!
CREATE OR REPLACE PROCEDURE upd_for_dept (
dept_in IN employee.department_id%TYPE
,newsal_in IN employee.salary%TYPE)
IS
CURSOR emp_cur IS
SELECT employee_id,salary,hire_date
FROM employee WHERE department_id = dept_in;
BEGIN
FOR rec IN emp_cur LOOP
adjust_compensation (rec, newsal_in);
UPDATE employee SET salary = rec.salary
WHERE employee_id = rec.employee_id;
END LOOP;
END upd_for_dept;
Row by row processing:
elegant but inefficient
Copyright
2014
Oracle
and/or
its
aliates.
All
rights
reserved.
|
Row
by
row
processing
of
DML
in
PL/SQL
Oracle
server
SQL
Engine
PL/SQL
RunGme
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
statement
executor
Performance penalty
for many context
switches
Copyright
2014
Oracle
and/or
its
aliates.
All
rights
reserved.
|
Bulk
Processing
in
PL/SQL
The
goal
is
straighrorward:
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
non-query
DML.
Use
with
inserts,
updates,
deletes
and
merges.
Move
data
from
collecGons
to
tables.
BULK
COLLECT
speeds
up
queries.
Can
be
used
with
all
kinds
of
queries:
implicit,
explicit,
staGc
and
dynamic.
Move
data
from
tables
into
collecGons.
Copyright
2014
Oracle
and/or
its
aliates.
All
rights
reserved.
|
Bulk
processing
with
FORALL
Oracle
server
SQL
Engine
PL/SQL
RunGme
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...
Procedural
statement
executor
Fewer context switches,
same SQL behavior
Copyright
2014
Oracle
and/or
its
aliates.
All
rights
reserved.
|
SQL
statement
executor
Update...
Update...
Update...
Update...
Update...
Update...
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
transacGon
and
rollback
segment
management
Same
number
of
individual
SQL
statements
will
be
executed.
Only
one
dierence:
BEFORE
and
AFTER
statement-level
triggers
only
re
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
2014
Oracle
and/or
its
aliates.
All
rights
reserved.
|
BULK
COLLECT
for
mul8-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
mulGple
rows
into
a
collecGon
with
a
single
fetch
(context
switch
to
the
SQL
engine).
Deposit
the
mulGple
rows
of
data
into
one
or
more
collec+ons.
NO_DATA_FOUND
is
not
raised
when
no
rows
are
fetched;
instead,
the
collecGon
is
empty.
The
"INTO"
collecGons
are
lled
sequenGally
from
index
value
1.
There
are
no
"gaps"
between
1
and
the
index
value
returned
by
the
COUNT
method.
Only
integer-indexed
collecGons
may
be
used.
No
need
to
iniGalize
or
extend
nested
tables
and
varrays.
Done
automaGcally
by
Oracle.
Copyright
2014
Oracle
and/or
its
aliates.
All
rights
reserved.
|
BULK
COLLECT
for
Implicit
Cursor
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.
DECLARE
TYPE employees_aat IS TABLE OF
employees%ROWTYPE;
l_employees employees_aat;
BEGIN
SELECT *
BULK COLLECT INTO l_employees
FROM employees;
FOR indx IN 1 .. l_employees.COUNT
LOOP
process_employee (l_employees(indx));
END LOOP;
END;
bulkcoll.sql
bulkcollect.tst
Copyright
2014
Oracle
and/or
its
aliates.
All
rights
reserved.
|
Limi8ng
retrieval
with
BULK
COLLECT
If
you
are
certain
that
your
table
with
never
have
more
than
N
rows,
use
a
VARRAY
(N)
to
hold
the
fetched
data.
If
that
limit
is
exceeded,
Oracle
will
raise
an
error.
This
is
not,
however,
a
very
common
scenario.
If
you
do
not
know
in
advance
how
many
rows
you
might
retrieve,
you
should:
1.
Declare
an
explicit
cursor.
2.
Fetch
BULK
COLLECT
with
the
LIMIT
clause.
Copyright
2014
Oracle
and/or
its
aliates.
All
rights
reserved.
|
Limit
#
of
rows
returned
by
BULK
COLLECT
Use
the
LIMIT
clause
with
the
INTO
to
manage
the
amount
of
memory
used
with
the
BULK
COLLECT
operaGon.
Denitely
the
preferred
approach
in
producGon
applicaGons
with
large
or
varying
datasets.
Always
check
the
contents
of
the
collecGon,
as
opposed
to
the
%NOTFOUND
aiributes,
to
determine
if
you
fetched
and
processed
all
rows.
bulklimit.sql
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
FETCH emps_in_dept_cur
BULK COLLECT INTO emps LIMIT 1000;
EXIT WHEN emps.COUNT = 0;
process_emps (emps);
END LOOP;
END bulk_with_limit;
Copyright
2014
Oracle
and/or
its
aliates.
All
rights
reserved.
|
When
to
convert
to
BULK
COLLECT
Prior
to
Oracle10g,
you
should
convert
all
mulGple
row
fetch
code
to
BULK
COLLECTs.
On
10.1
and
higher,
the
opGmizer
will
automaGcally
opGmize
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
operaGons.
seem
to
be
running
fast
enough.
Explicit
BULK
COLLECTs
will
usually
run
a
li6le
faster
than
cursor
for
loops
opGmized
to
BC.
10g_optimize_cfl.sql
Copyright
2014
Oracle
and/or
its
aliates.
All
rights
reserved.
|
Use
the
FORALL
Bulk
Bind
Statement
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;
Instead
of
execuGng
repeGGve,
individual
DML
statements,
load
up
variables
into
an
array
and
bind
that
array
into
a
"template"
DML
statement.
Only
a
single
DML
statement
is
allowed
per
FORALL
Can
be
used
with
staGc
and
dynamic
SQL
Copyright
2014
Oracle
and/or
its
aliates.
All
rights
reserved.
|
bulkGming.sql
Key
features
and
limita8ons
of
FORALL
Use
SQL%BULK_ROWCOUNT
to
determine
the
number
of
rows
modied
by
each
statement
executed.
SQL%ROWCOUNT
returns
total
number
of
rows
modied
by
the
enGre
FORALL.
Use
SAVE_EXCEPTIONS
and
SQL%BULK_EXCEPTIONS
to
conGnue
past
excepGons
in
any
of
the
statements.
Granular
error
recovery
and
logging
Can
also
use
LOG
ERRORS
in
DML
statement
in
place
of
SAVE
EXCEPTIONS
Use
INDICES
OF
or
VALUES
OF
when
bind
array
is
sparse.
bulk_rowcount.sql
bulkexc.sql
10g_indices_of*.sql
10g_values_of*.sql
Copyright
2014
Oracle
and/or
its
aliates.
All
rights
reserved.
|
From
OldFashioned
to
Modern
Code
TradiGonal
PL/SQL
code
oten
involves
a
cursor
FOR
loop
and
mulGple
DML
statements
inside
the
loop.
An
integrated,
row-by-row
approach
with
the
opGon
of
trapping
and
conGnuing
past
excepGons.
Elegant
and
exible,
but
slow.
Bulk
processing
means
a
switch
to
"phased
approach".
Compared
to
the
funcGon
result
cache,
your
code
will
require
much
more
extensive
reworking.
Code
will
get
more
complex
and
larger
in
size.
That's
the
price
we
pay
for
changing
the
way
PL/SQL
communicates
with
SQL.
Copyright
2014
Oracle
and/or
its
aliates.
All
rights
reserved.
|
A
phased
approach
with
bulk
processing
Change
from
integrated,
row-by-row
approach
to
a
phased
approach.
RelaGonal
Table(s)
Phase
1:
Bulk
collect
from
table(s)
to
collecGon
Phase
2:
Modify
contents
of
collecGon
according
to
requirements
RelaGonal
Table
Phase
3:
FORALL
from
collecGon
to
table
Copyright
2014
Oracle
and/or
its
aliates.
All
rights
reserved.
|
c_to_bulk_0.sql
c_to_bulk_5.sql
Bulk
Processing
Conclusions
Most
important
performance
tuning
feature
in
PL/SQL.
Almost
always
the
fastest
way
to
execute
mulG-row
SQL
operaGons
in
PL/SQL.
You
trade
o
increased
complexity
of
code
for
dramaGcally
faster
execuGon.
But
in
Oracle
Database
10g
and
above,
the
compiler
will
automaGcally
opGmize
cursor
FOR
loops
to
BULK
COLLECT
eciency.
No
need
to
convert
unless
the
loop
contains
DML.
Watch
out
for
the
impact
on
PGA/UGA
memory!
Copyright
2014
Oracle
and/or
its
aliates.
All
rights
reserved.
|
The
NOCOPY
hint
By
default,
Oracle
passes
all
OUT
and
IN
OUT
arguments
by
value,
not
reference.
This
means
that
OUT
and
IN
OUT
arguments
always
involve
some
copying
of
data.
All
IN
arguments
are
always
passed
by
reference
(no
copying).
With
NOCOPY,
you
turn
o
the
copy
process.
But
it
comes
with
a
risk:
Oracle
will
not
automaGcally
"rollback"
or
reverse
changes
made
to
your
variables
if
the
NOCOPY-ed
program
raises
an
excepGon.
nocopy*.*
string_nocopy.*
Copyright
2014
Oracle
and/or
its
aliates.
All
rights
reserved.
|
Op8mizing
Func8on
Execu8on
in
SQL
That
seems
like
an
awfully
good
idea!
Two
methods:
WITH
clause
that
denes
a
funcGon
UDF
pragma
WITH
FUNCTION:
dene
a
funcGon
directly
within
your
SQL
statement.
Most
helpful
for
avoiding
redundancy
in
long
SQL
statements
UDF
pragma
declaraGvely
tells
the
compiler
to
"prepare"
the
funcGon
for
execuGon
from
SQL.
Reduces
the
cost
of
the
context
switch
12c_with_funcGon*.sql
12c_udf*.sql
Copyright
2014
Oracle
and/or
its
aliates.
All
rights
reserved.
|
High
Performance
PL/SQL
a
summary
Make
sure
you
take
advantage
of
the
most
important
opGmizaGon
features:
BULK
COLLECT
and
FORALL
Cached
data
PrioriGze
maintainability
of
code
over
obsessively
opGmized
code.
Then
idenGfy
boilenecks
and
apply
the
more
specialized
opGmizaGon
techniques.
NOCOPY
is
a
ne
example,
though
you
can
usually
apply
this
proacGvely
with
small
risk.
UDF
pragma
key
point
is
to
analyze
how
your
funcGon
would
be
used
Copyright
2014
Oracle
and/or
its
aliates.
All
rights
reserved.
|
Q+A
Copyright
2014
Oracle
and/or
its
aliates.
All
rights
reserved.
|
31
Keep
Learning
with
Training
from
Oracle
University
Hands-on
training
delivered
in-class
or
online
by
tenured
instructors
around
the
world
New
subscripGon-based
learning
services
to
give
you
any-Gme
access
to
training
CerGcaGon
programs
to
validate
your
skills
educa8on.oracle.com
Copyright
2014
Oracle
and/or
its
aliates.
All
rights
reserved.
|
32
Copyright
2014
Oracle
and/or
its
aliates.
All
rights
reserved.
|
33
Copyright
2014
Oracle
and/or
its
aliates.
All
rights
reserved.
|
34
35