SlideShare a Scribd company logo
Copyright © 2014 Oracle and/or its affiliates. All rights reserved. |
Oracle PL/SQL Performance Features
by Steven Feuerstein
Architect, Oracle Corporation
email: steven.feuerstein@oracle.com
twitter: sfonplsql
blog: stevenfeuersteinonplsql.blogspot.com
Turbocharge SQL
Performance in PL/SQL with
Bulk Processing
Copyright © 2014 Oracle and/or its affiliates. All rights reserved. |
Resources for Oracle Database Developers
• Official home of PL/SQL - oracle.com/plsql
• SQL-PL/SQL discussion forum on OTN
https://community.oracle.com/community/database/developer-tools/sql_and_pl_sql
• PL/SQL and EBR blog by Bryn Llewellyn - https://blogs.oracle.com/plsql-and-ebr
• Oracle Learning Library - oracle.com/oll
• Weekly PL/SQL and SQL quizzes, and more - plsqlchallenge.oracle.com
• Ask Tom - asktom.oracle.com – 'nuff said
• LiveSQL - livesql.oracle.com – script repository and 12/7 12c database
• oracle-developer.net - great content from Adrian Billington
• oracle-base.com - great content from Tim Hall
Copyright © 2014 Oracle and/or its affiliates. All rights reserved. |
What’s the problem with this code?
• We have, on average, 10,000 employees per department.
CREATE OR REPLACE PROCEDURE upd_for_dept (
dept_in IN employees.department_id%TYPE
,newsal_in IN employees.salary%TYPE)
IS
CURSOR emp_cur IS
SELECT employee_id,salary,hire_date
FROM employees 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;
Copyright © 2014 Oracle and/or its affiliates. All rights reserved. |
Row-by-row = Slow-by-slow?
• Many PL/SQL blocks execute the same SQL statement repeatedly, with
different bind values.
–Retrieves data one row at a time.
–Performs same DML operation for each row retrieved.
• The SQL engine does a lot to optimize performance, but you this row-by-
row processing is inherently slow.
– But, but...aren't SQL and PL/SQL supposed to be very tightly integrated? Let's take a
look "under the covers.
Copyright © 2014 Oracle and/or its affiliates. All rights reserved. |
Oracle server
PL/SQL Runtime Engine SQL Engine
PL/SQL block
Procedural
statement
executor
SQL statement
executor
FOR rec IN emp_cur LOOP
UPDATE employee
SET salary = ...
WHERE employee_id =
rec.employee_id;
END LOOP;
Performance penalty for many
“context switches”
Repetitive statement processing from PL/SQL
Copyright © 2014 Oracle and/or its affiliates. All rights reserved. |
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 non-query 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 © 2014 Oracle and/or its affiliates. All rights reserved. |
Bulk processing with FORALL
Oracle server
PL/SQL Runtime Engine SQL Engine
PL/SQL block
Procedural
statement
executor
SQL statement
executor
FORALL indx IN
list_of_emps.FIRST..
list_of_emps.LAST
UPDATE employee
SET salary = ...
WHERE employee_id =
list_of_emps(indx);
Fewer context switches,
same SQL behavior
Update...
Update...
Update...
Update...
Update...
Update...
Update...
Update...
Update...
Update...
Update...
Update...
Copyright © 2014 Oracle and/or its affiliates. All rights reserved. |
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 © 2014 Oracle and/or its affiliates. All rights reserved. |
Impact of bulk processing on memory consumption
• As you will soon see, to utilize bulk processing, you must utilize collections.
• If you are not careful about how you fill and manage these collections, your
users can run into ugly memory errors.
9
System Global Area (SGA) of RDBMS Instance
Shared Pool
Large Pool
Reserved Pool
show_empscalc_totals upd_salaries
Select *
from emp
Shared SQL
Pre-parsed
Update emp
Set sal=...
Library cache
Session 1 memory
UGA – User Global Area
PGA – Process Global Area
emp_rec emp%rowtype;
tot_tab pkg.tottabtype;
Session 2 memory
UGA – User Global Area
PGA – Process Global Area
emp_rec emp%rowtype;
tot_tab pkg.tottabtype;
Session 1 Session 2
Copyright © 2014 Oracle and/or its affiliates. All rights reserved. |
How PL/SQL uses the SGA, PGA and UGA
• The SGA contains information that can be shared across schemas connected to the
instance.
– From the PL/SQL perspective, this is limited to package static constants.
• The Process Global Area contains session-specific data that is released when the
current server call terminates.
– Local data
• The User Global Area contains session-specific data that persists across server call
boundaries
– Package-level data
PACKAGE Pkg is
Nonstatic_Constant CONSTANT PLS_INTEGER := My_Sequence.Nextval;
Static_Constant CONSTANT PLS_INTEGER := 42;
END Pkg;
top_pga.sql
Copyright © 2014 Oracle and/or its affiliates. All rights reserved. |
Calculating PGA and UGA Consumption
• 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.
show_pga_uga.sql
grantv$.sql
plsql_memory.pkg
plsql_memory_demo.sql
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')
BEGIN
plsql_memory.start_analysis;
run_my_application;
plsql_memory.show_memory_usage;
END;
Copyright © 2014 Oracle and/or its affiliates. All rights reserved. |
BULK COLLECT for multi-row querying
• 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.
• NO_DATA_FOUND is not raised when no rows are fetched; instead, the collection is
empty.
• 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.
• Only integer-indexed collections may be used.
• No need to initialize or extend nested tables and varrays. Done automatically by Oracle.
SELECT * BULK COLLECT INTO collection(s) FROM table;
FETCH cur BULK COLLECT INTO collection(s);
EXECUTE IMMEDIATE query BULK COLLECT INTO collection(s);
Copyright © 2014 Oracle and/or its affiliates. All rights reserved. |
BULK COLLECT for Implicit Cursor
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
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 © 2014 Oracle and/or its affiliates. All rights reserved. |
What's wrong with this code?
• As data volume grows, PGA memory requirement grows.
Eventually....KABOOM!
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;
Copyright © 2014 Oracle and/or its affiliates. All rights reserved. |
Limiting 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 affiliates. All rights reserved. |
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
FETCH emps_in_dept_cur
BULK COLLECT INTO emps LIMIT 1000;
EXIT WHEN emps.COUNT = 0;
process_emps (emps);
END LOOP;
CLOSE emps_in_dept_cur;
END bulk_with_limit;
Use the LIMIT clause with the INTO to
manage the amount of memory used
with the BULK COLLECT operation.
Definitely the preferred approach in
production applications with large or
varying datasets.
bulklimit.sql
Copyright © 2014 Oracle and/or its affiliates. All rights reserved. |
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 © 2014 Oracle and/or its affiliates. All rights reserved. |
Terminating loops containing BULK COLLECT
• You will need to break the habit of checking %NOTFOUND right after the
fetch.
– You might skip processing some of your data.
• Instead, do one of the following:
– At the end of the loop, check %NOTFOUND.
– Right after fetch, exit when collection.COUNT = 0.
– At end of loop, exit when collection.COUNT < limit.
LOOP
FETCH my_cursor BULK COLLECT INTO l_collection LIMIT 100;
EXIT WHEN my_cursor%NOTFOUND; BAD IDEA
bulklimit_stop.sql
Copyright © 2014 Oracle and/or its affiliates. All rights reserved. |
When to convert to BULK COLLECT
• Prior to Oracle10g, you should convert all multiple row fetch code to BULK
COLLECTs.
• On 10.1 and higher, the optimizer will automatically optimize 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.
– seem to be running fast enough.
• Explicit BULK COLLECTs will usually run a little faster than cursor for loops
optimized to BC.
10g_optimize_cfl.sql
Copyright © 2014 Oracle and/or its affiliates. All rights reserved. |
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.
• Leave it to the optimizer to speed up "read only" cursor FOR loops.
Copyright © 2014 Oracle and/or its affiliates. All rights reserved. |
FORALL Agenda
• Introduction to FORALL
• Using the SQL%BULK_ROWCOUNT
• Referencing fields of collections of records
• Using FORALL with sparsely-filled collections
• Handling errors raised during execution of FORALL
Copyright © 2014 Oracle and/or its affiliates. All rights reserved. |
Use FORALL for repeated DML operations
• Convert loops that contain inserts, updates, deletes or merges to FORALL
statements.
• Header (usually) looks identical to a numeric FOR loop.
– Implicitly declared integer iterator
– At least one bind array that uses the iterator as its index value.
– Use INDICES OF and VALUES OF when bind arrays are sparse
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;
forall_timing.sql
forall_examples.sql
Copyright © 2014 Oracle and/or its affiliates. All rights reserved. |
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 © 2014 Oracle and/or its affiliates. All rights reserved. |
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 © 2014 Oracle and/or its affiliates. All rights reserved. |
FORALL and collections of records
• Prior to 11g, you cannot reference a field of a record in FORALL.
• You must instead break data into separate collections, or...
• You can also perform record-level inserts and updates.
• In Oracle Database 11g, this restriction was lifted.
11g_field_of_record.sql
Copyright © 2014 Oracle and/or its affiliates. All rights reserved. |
Using FORALL with Sparse Collections
• Prior to 10.2, the binding arrays in a FORALL statement must be
sequentially filled.
– Using the IN low .. high syntax.
• Now, however, you can bind sparse collections by using INDICES OF and
VALUES OF in the FORALL header.
10g_indices_of*.sql
10g_values_of*.sql
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);
Copyright © 2014 Oracle and/or its affiliates. All rights reserved. |
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!
Copyright © 2014 Oracle and/or its affiliates. All rights reserved. |
SAVE EXCEPTIONS and FORALL
• The SAVE EXCEPTIONS clause tells Oracle to save exception information and
continue processing all of the DML statements.
• When the FORALL statement completes, if at least one exception occurred,
Oracle then raises ORA-24381.
• You then check the contents of SQL%BULK_EXCEPTIONS.
PROCEDURE upd_for_dept (newsal_in IN NUMBER,
list_of_emps_in IN DBMS_SQL.NUMBER_TABLE) IS
BEGIN
FORALL indx IN list_of_emps_in.FIRST .. list_of_emps_in.LAST
SAVE EXCEPTIONS
UPDATE employees
SET salary = newsal_in
WHERE employee_id = list_of_emps_in (indx);
END;
Copyright © 2014 Oracle and/or its affiliates. All rights reserved. |
Example: FORALL with SAVE EXCEPTIONS
• Add SAVE EXCEPTIONS to enable FORALL to suppress
errors at the statement level.
CREATE OR REPLACE PROCEDURE load_books (books_in IN book_obj_list_t)
IS
bulk_errors EXCEPTION;
PRAGMA EXCEPTION_INIT ( bulk_errors, -24381 );
BEGIN
FORALL indx IN books_in.FIRST..books_in.LAST
SAVE EXCEPTIONS
INSERT INTO book values (books_in(indx));
EXCEPTION
WHEN bulk_errors THEN
FOR indx in 1..SQL%BULK_EXCEPTIONS.COUNT
LOOP
log_error (SQL%BULK_EXCEPTIONS(indx).ERROR_INDEX
, SQL%BULK_EXCEPTIONS(indx).ERROR_CODE);
END LOOP;
END;
Allows processing of all
statements, even after an error
occurs.
Iterate through "pseudo-
collection" of errors.
bulkexc.sql
If any exception is
encountered, Oracle raises
-24381 when done.
Copyright © 2014 Oracle and/or its affiliates. All rights reserved. |
SAVE EXCEPTIONS in Detail
• For each exception raised, Oracle populates the SQL%BULK_EXCEPTIONS
pseudo-collection of records.
– The record has two fields : ERROR_INDEX and ERROR_CODE.
– ERROR_INDEX: the index in the bind array for which the error occurred.
– ERROR_CODE: the number (positive) for the error that was raised
• It's a pseudo-collection and only supports a single method: COUNT.
• So you iterate from 1 to SQL%BULK_EXCEPTIONS.COUNT to get
information about each error.
• Unfortunately, it does not store the error message.
Copyright © 2014 Oracle and/or its affiliates. All rights reserved. |
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
• Challenges include:
– With multiple DML statements in loop, how do you "communicate" from one to the
other?
– Avoid excessive PGA consumption
Copyright © 2014 Oracle and/or its affiliates. All rights reserved. |
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 © 2014 Oracle and/or its affiliates. All rights reserved. |
A phased approach with bulk processing
• Change from integrated, row-by-row approach to a phased approach.
Relational
Table(s)
Relational
Table
Phase 1: Bulk collect from table(s) to collection
Phase 3: FORALL from collection to table
Phase 2: Modify contents of collection according
to requirements
Copyright © 2014 Oracle and/or its affiliates. All rights reserved. |
Translating phases into code
• The cfl_to_bulk_5.sql file contains the converted program, following the
phased approach.
cfl_to_bulk_0.sql
cfl_to_bulk_5_11g.sql
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;
Phase 1:
Get Data
Phase 3:
Push Data
Phase 2:
Massage Data
Phase 3:
Push Data
Copyright © 2014 Oracle and/or its affiliates. All rights reserved. |
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.
– Look that for prime anti-pattern: Loops containing non-query DML.
• 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 © 2014 Oracle and/or its affiliates. All rights reserved. | 36
Turbocharge SQL Performance in PL/SQL with Bulk Processing

More Related Content

What's hot (20)

ODP
Ms sql-server
Md.Mojibul Hoque
 
PPT
09 Managing Dependencies
rehaniltifat
 
PPTX
Oracle SQL Developer Tips & Tricks
Jeff Smith
 
PDF
Oracle db performance tuning
Simon Huang
 
PDF
Postgresql database administration volume 1
Federico Campoli
 
PPT
Explain that explain
Fabrizio Parrella
 
PDF
Advance database system (part 3)
Abdullah Khosa
 
PPTX
Understanding my database through SQL*Plus using the free tool eDB360
Carlos Sierra
 
PDF
Error Management Features of PL/SQL
Steven Feuerstein
 
PDF
DB Time, Average Active Sessions, and ASH Math - Oracle performance fundamentals
John Beresniewicz
 
PPTX
Oracle Database Performance Tuning Basics
nitin anjankar
 
PPTX
Oracle database performance tuning
Yogiji Creations
 
PDF
Harnessing the Power of Optimizer Hints
Maria Colgan
 
PDF
Window functions in MySQL 8.0
Mydbops
 
PDF
How to Analyze and Tune MySQL Queries for Better Performance
oysteing
 
PDF
SQL
kaushal123
 
PDF
Chapter 4 Structured Query Language
Eddyzulham Mahluzydde
 
PDF
Using Optimizer Hints to Improve MySQL Query Performance
oysteing
 
PDF
Oracle Performance Tuning Fundamentals
Enkitec
 
PPTX
SQL
Vineeta Garg
 
Ms sql-server
Md.Mojibul Hoque
 
09 Managing Dependencies
rehaniltifat
 
Oracle SQL Developer Tips & Tricks
Jeff Smith
 
Oracle db performance tuning
Simon Huang
 
Postgresql database administration volume 1
Federico Campoli
 
Explain that explain
Fabrizio Parrella
 
Advance database system (part 3)
Abdullah Khosa
 
Understanding my database through SQL*Plus using the free tool eDB360
Carlos Sierra
 
Error Management Features of PL/SQL
Steven Feuerstein
 
DB Time, Average Active Sessions, and ASH Math - Oracle performance fundamentals
John Beresniewicz
 
Oracle Database Performance Tuning Basics
nitin anjankar
 
Oracle database performance tuning
Yogiji Creations
 
Harnessing the Power of Optimizer Hints
Maria Colgan
 
Window functions in MySQL 8.0
Mydbops
 
How to Analyze and Tune MySQL Queries for Better Performance
oysteing
 
Chapter 4 Structured Query Language
Eddyzulham Mahluzydde
 
Using Optimizer Hints to Improve MySQL Query Performance
oysteing
 
Oracle Performance Tuning Fundamentals
Enkitec
 

Viewers also liked (19)

PPTX
The Amazing and Elegant PL/SQL Function Result Cache
Steven Feuerstein
 
PPTX
Take Full Advantage of the Oracle PL/SQL Compiler
Steven Feuerstein
 
PDF
All About PL/SQL Collections
Steven Feuerstein
 
PDF
Impact Analysis with PL/Scope
Steven Feuerstein
 
PPTX
utPLSQL: Unit Testing for Oracle PL/SQL
Steven Feuerstein
 
PDF
Artigo Jornal Económico "ValueKeep quer captar novas subscrições"
Primavera Business Software Solutions SA
 
PPTX
PL/SQL All the Things in Oracle SQL Developer
Jeff Smith
 
PDF
AMIS - Can collections speed up your PL/SQL?
Getting value from IoT, Integration and Data Analytics
 
PDF
APEX Developers : Do More With LESS !
Roel Hartman
 
PPT
02 Writing Executable Statments
rehaniltifat
 
PDF
Designpatternscard
kyutae.kang
 
DOC
Transcript of Presentation(Simple & Compound Interest) for Audience
rehaniltifat
 
PPT
Case study what you can take to the bank(primary functions)
rehaniltifat
 
PPT
11 Understanding and Influencing the PL/SQL Compilar
rehaniltifat
 
PDF
Troubleshooting APEX Performance Issues
Roel Hartman
 
PPT
07 Using Oracle-Supported Package in Application Development
rehaniltifat
 
PPT
03 Writing Control Structures, Writing with Compatible Data Types Using Expli...
rehaniltifat
 
PPT
Oracle query optimizer
Smitha Padmanabhan
 
PPT
08 Dynamic SQL and Metadata
rehaniltifat
 
The Amazing and Elegant PL/SQL Function Result Cache
Steven Feuerstein
 
Take Full Advantage of the Oracle PL/SQL Compiler
Steven Feuerstein
 
All About PL/SQL Collections
Steven Feuerstein
 
Impact Analysis with PL/Scope
Steven Feuerstein
 
utPLSQL: Unit Testing for Oracle PL/SQL
Steven Feuerstein
 
Artigo Jornal Económico "ValueKeep quer captar novas subscrições"
Primavera Business Software Solutions SA
 
PL/SQL All the Things in Oracle SQL Developer
Jeff Smith
 
AMIS - Can collections speed up your PL/SQL?
Getting value from IoT, Integration and Data Analytics
 
APEX Developers : Do More With LESS !
Roel Hartman
 
02 Writing Executable Statments
rehaniltifat
 
Designpatternscard
kyutae.kang
 
Transcript of Presentation(Simple & Compound Interest) for Audience
rehaniltifat
 
Case study what you can take to the bank(primary functions)
rehaniltifat
 
11 Understanding and Influencing the PL/SQL Compilar
rehaniltifat
 
Troubleshooting APEX Performance Issues
Roel Hartman
 
07 Using Oracle-Supported Package in Application Development
rehaniltifat
 
03 Writing Control Structures, Writing with Compatible Data Types Using Expli...
rehaniltifat
 
Oracle query optimizer
Smitha Padmanabhan
 
08 Dynamic SQL and Metadata
rehaniltifat
 
Ad

Similar to Turbocharge SQL Performance in PL/SQL with Bulk Processing (20)

PDF
High Performance PL/SQL
Steven Feuerstein
 
PPT
Oracle - SQL-PL/SQL context switching
Smitha Padmanabhan
 
PPT
01 oracle architecture
Smitha Padmanabhan
 
PDF
Exploring plsql new features best practices september 2013
Andrejs Vorobjovs
 
PDF
Developer day v2
AiougVizagChapter
 
PPTX
Oracle Database 12c - The Best Oracle Database 12c Tuning Features for Develo...
Alex Zaballa
 
PPT
Oracle SQL, PL/SQL Performance tuning
Smitha Padmanabhan
 
PDF
ETL with Clustered Columnstore - PASS Summit 2014
Niko Neugebauer
 
PPTX
Day 6.pptx
atreesgalaxy
 
PDF
Oaktable World 2014 Kevin Closson: SLOB – For More Than I/O!
Kyle Hailey
 
PPTX
AWR and ASH Deep Dive
Kellyn Pot'Vin-Gorman
 
PPT
plsql les06
sasa_eldoby
 
PPTX
Analyze database system using a 3 d method
Ajith Narayanan
 
PPTX
Lecture 3.2_Subprogrammm - Function.pptx
pproychd
 
PPTX
Lecture 3.2_Subprogrammm - Function.pptx
pproychd
 
PDF
Modernizing your database with SQL Server 2019
Antonios Chatzipavlis
 
PDF
PL/SQL CURSORES
Richard Eliseo Mendoza Gafaro
 
PDF
AWR and ASH in an EM12c World
Kellyn Pot'Vin-Gorman
 
PPTX
Kellyn Pot'Vin-Gorman - Awr and Ash
gaougorg
 
PPTX
Oracle Database 12c - New Features for Developers and DBAs
Alex Zaballa
 
High Performance PL/SQL
Steven Feuerstein
 
Oracle - SQL-PL/SQL context switching
Smitha Padmanabhan
 
01 oracle architecture
Smitha Padmanabhan
 
Exploring plsql new features best practices september 2013
Andrejs Vorobjovs
 
Developer day v2
AiougVizagChapter
 
Oracle Database 12c - The Best Oracle Database 12c Tuning Features for Develo...
Alex Zaballa
 
Oracle SQL, PL/SQL Performance tuning
Smitha Padmanabhan
 
ETL with Clustered Columnstore - PASS Summit 2014
Niko Neugebauer
 
Day 6.pptx
atreesgalaxy
 
Oaktable World 2014 Kevin Closson: SLOB – For More Than I/O!
Kyle Hailey
 
AWR and ASH Deep Dive
Kellyn Pot'Vin-Gorman
 
plsql les06
sasa_eldoby
 
Analyze database system using a 3 d method
Ajith Narayanan
 
Lecture 3.2_Subprogrammm - Function.pptx
pproychd
 
Lecture 3.2_Subprogrammm - Function.pptx
pproychd
 
Modernizing your database with SQL Server 2019
Antonios Chatzipavlis
 
AWR and ASH in an EM12c World
Kellyn Pot'Vin-Gorman
 
Kellyn Pot'Vin-Gorman - Awr and Ash
gaougorg
 
Oracle Database 12c - New Features for Developers and DBAs
Alex Zaballa
 
Ad

More from Steven Feuerstein (13)

PDF
New(er) Stuff in PL/SQL
Steven Feuerstein
 
PDF
Six simple steps to unit testing happiness
Steven Feuerstein
 
PDF
Speakers at Nov 2019 PL/SQL Office Hours session
Steven Feuerstein
 
PDF
New Stuff in the Oracle PL/SQL Language
Steven Feuerstein
 
PDF
AskTOM Office Hours on Database Triggers
Steven Feuerstein
 
PDF
PL/SQL Guilty Pleasures
Steven Feuerstein
 
PDF
Oracle Application Express and PL/SQL: a world-class combo
Steven Feuerstein
 
PDF
JSON and PL/SQL: A Match Made in Database
Steven Feuerstein
 
PDF
OLD APEX and PL/SQL
Steven Feuerstein
 
PDF
Oracle PL/SQL 12c and 18c New Features + RADstack + Community Sites
Steven Feuerstein
 
PDF
AskTOM Office Hours - Dynamic SQL in PL/SQL
Steven Feuerstein
 
PPT
Database Developers: the most important developers on earth?
Steven Feuerstein
 
PDF
Unit Testing Oracle PL/SQL Code: utPLSQL, Excel and More
Steven Feuerstein
 
New(er) Stuff in PL/SQL
Steven Feuerstein
 
Six simple steps to unit testing happiness
Steven Feuerstein
 
Speakers at Nov 2019 PL/SQL Office Hours session
Steven Feuerstein
 
New Stuff in the Oracle PL/SQL Language
Steven Feuerstein
 
AskTOM Office Hours on Database Triggers
Steven Feuerstein
 
PL/SQL Guilty Pleasures
Steven Feuerstein
 
Oracle Application Express and PL/SQL: a world-class combo
Steven Feuerstein
 
JSON and PL/SQL: A Match Made in Database
Steven Feuerstein
 
OLD APEX and PL/SQL
Steven Feuerstein
 
Oracle PL/SQL 12c and 18c New Features + RADstack + Community Sites
Steven Feuerstein
 
AskTOM Office Hours - Dynamic SQL in PL/SQL
Steven Feuerstein
 
Database Developers: the most important developers on earth?
Steven Feuerstein
 
Unit Testing Oracle PL/SQL Code: utPLSQL, Excel and More
Steven Feuerstein
 

Recently uploaded (20)

PDF
“Scaling i.MX Applications Processors’ Native Edge AI with Discrete AI Accele...
Edge AI and Vision Alliance
 
PDF
FME as an Orchestration Tool with Principles From Data Gravity
Safe Software
 
PDF
Optimizing the trajectory of a wheel loader working in short loading cycles
Reno Filla
 
PDF
My Journey from CAD to BIM: A True Underdog Story
Safe Software
 
PDF
5 Things to Consider When Deploying AI in Your Enterprise
Safe Software
 
PDF
“MPU+: A Transformative Solution for Next-Gen AI at the Edge,” a Presentation...
Edge AI and Vision Alliance
 
PDF
UiPath Agentic AI ile Akıllı Otomasyonun Yeni Çağı
UiPathCommunity
 
PDF
From Chatbot to Destroyer of Endpoints - Can ChatGPT Automate EDR Bypasses (1...
Priyanka Aash
 
PDF
Automating the Geo-Referencing of Historic Aerial Photography in Flanders
Safe Software
 
PPTX
Curietech AI in action - Accelerate MuleSoft development
shyamraj55
 
PDF
Java 25 and Beyond - A Roadmap of Innovations
Ana-Maria Mihalceanu
 
PPTX
Enabling the Digital Artisan – keynote at ICOCI 2025
Alan Dix
 
PDF
Python Conference Singapore - 19 Jun 2025
ninefyi
 
PPTX
MARTSIA: A Tool for Confidential Data Exchange via Public Blockchain - Poster...
Michele Kryston
 
PPTX
UserCon Belgium: Honey, VMware increased my bill
stijn40
 
PDF
Database Benchmarking for Performance Masterclass: Session 1 - Benchmarking F...
ScyllaDB
 
PDF
Redefining Work in the Age of AI - What to expect? How to prepare? Why it mat...
Malinda Kapuruge
 
PDF
Kubernetes - Architecture & Components.pdf
geethak285
 
PDF
LLM Search Readiness Audit - Dentsu x SEO Square - June 2025.pdf
Nick Samuel
 
PPTX
Smarter Governance with AI: What Every Board Needs to Know
OnBoard
 
“Scaling i.MX Applications Processors’ Native Edge AI with Discrete AI Accele...
Edge AI and Vision Alliance
 
FME as an Orchestration Tool with Principles From Data Gravity
Safe Software
 
Optimizing the trajectory of a wheel loader working in short loading cycles
Reno Filla
 
My Journey from CAD to BIM: A True Underdog Story
Safe Software
 
5 Things to Consider When Deploying AI in Your Enterprise
Safe Software
 
“MPU+: A Transformative Solution for Next-Gen AI at the Edge,” a Presentation...
Edge AI and Vision Alliance
 
UiPath Agentic AI ile Akıllı Otomasyonun Yeni Çağı
UiPathCommunity
 
From Chatbot to Destroyer of Endpoints - Can ChatGPT Automate EDR Bypasses (1...
Priyanka Aash
 
Automating the Geo-Referencing of Historic Aerial Photography in Flanders
Safe Software
 
Curietech AI in action - Accelerate MuleSoft development
shyamraj55
 
Java 25 and Beyond - A Roadmap of Innovations
Ana-Maria Mihalceanu
 
Enabling the Digital Artisan – keynote at ICOCI 2025
Alan Dix
 
Python Conference Singapore - 19 Jun 2025
ninefyi
 
MARTSIA: A Tool for Confidential Data Exchange via Public Blockchain - Poster...
Michele Kryston
 
UserCon Belgium: Honey, VMware increased my bill
stijn40
 
Database Benchmarking for Performance Masterclass: Session 1 - Benchmarking F...
ScyllaDB
 
Redefining Work in the Age of AI - What to expect? How to prepare? Why it mat...
Malinda Kapuruge
 
Kubernetes - Architecture & Components.pdf
geethak285
 
LLM Search Readiness Audit - Dentsu x SEO Square - June 2025.pdf
Nick Samuel
 
Smarter Governance with AI: What Every Board Needs to Know
OnBoard
 

Turbocharge SQL Performance in PL/SQL with Bulk Processing

  • 1. Copyright © 2014 Oracle and/or its affiliates. All rights reserved. | Oracle PL/SQL Performance Features by Steven Feuerstein Architect, Oracle Corporation email: [email protected] twitter: sfonplsql blog: stevenfeuersteinonplsql.blogspot.com Turbocharge SQL Performance in PL/SQL with Bulk Processing
  • 2. Copyright © 2014 Oracle and/or its affiliates. All rights reserved. | Resources for Oracle Database Developers • Official home of PL/SQL - oracle.com/plsql • SQL-PL/SQL discussion forum on OTN https://community.oracle.com/community/database/developer-tools/sql_and_pl_sql • PL/SQL and EBR blog by Bryn Llewellyn - https://blogs.oracle.com/plsql-and-ebr • Oracle Learning Library - oracle.com/oll • Weekly PL/SQL and SQL quizzes, and more - plsqlchallenge.oracle.com • Ask Tom - asktom.oracle.com – 'nuff said • LiveSQL - livesql.oracle.com – script repository and 12/7 12c database • oracle-developer.net - great content from Adrian Billington • oracle-base.com - great content from Tim Hall
  • 3. Copyright © 2014 Oracle and/or its affiliates. All rights reserved. | What’s the problem with this code? • We have, on average, 10,000 employees per department. CREATE OR REPLACE PROCEDURE upd_for_dept ( dept_in IN employees.department_id%TYPE ,newsal_in IN employees.salary%TYPE) IS CURSOR emp_cur IS SELECT employee_id,salary,hire_date FROM employees 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;
  • 4. Copyright © 2014 Oracle and/or its affiliates. All rights reserved. | Row-by-row = Slow-by-slow? • Many PL/SQL blocks execute the same SQL statement repeatedly, with different bind values. –Retrieves data one row at a time. –Performs same DML operation for each row retrieved. • The SQL engine does a lot to optimize performance, but you this row-by- row processing is inherently slow. – But, but...aren't SQL and PL/SQL supposed to be very tightly integrated? Let's take a look "under the covers.
  • 5. Copyright © 2014 Oracle and/or its affiliates. All rights reserved. | Oracle server PL/SQL Runtime Engine SQL Engine PL/SQL block Procedural statement executor SQL statement executor FOR rec IN emp_cur LOOP UPDATE employee SET salary = ... WHERE employee_id = rec.employee_id; END LOOP; Performance penalty for many “context switches” Repetitive statement processing from PL/SQL
  • 6. Copyright © 2014 Oracle and/or its affiliates. All rights reserved. | 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 non-query 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.
  • 7. Copyright © 2014 Oracle and/or its affiliates. All rights reserved. | Bulk processing with FORALL Oracle server PL/SQL Runtime Engine SQL Engine PL/SQL block Procedural statement executor SQL statement executor FORALL indx IN list_of_emps.FIRST.. list_of_emps.LAST UPDATE employee SET salary = ... WHERE employee_id = list_of_emps(indx); Fewer context switches, same SQL behavior Update... Update... Update... Update... Update... Update... Update... Update... Update... Update... Update... Update...
  • 8. Copyright © 2014 Oracle and/or its affiliates. All rights reserved. | 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
  • 9. Copyright © 2014 Oracle and/or its affiliates. All rights reserved. | Impact of bulk processing on memory consumption • As you will soon see, to utilize bulk processing, you must utilize collections. • If you are not careful about how you fill and manage these collections, your users can run into ugly memory errors. 9 System Global Area (SGA) of RDBMS Instance Shared Pool Large Pool Reserved Pool show_empscalc_totals upd_salaries Select * from emp Shared SQL Pre-parsed Update emp Set sal=... Library cache Session 1 memory UGA – User Global Area PGA – Process Global Area emp_rec emp%rowtype; tot_tab pkg.tottabtype; Session 2 memory UGA – User Global Area PGA – Process Global Area emp_rec emp%rowtype; tot_tab pkg.tottabtype; Session 1 Session 2
  • 10. Copyright © 2014 Oracle and/or its affiliates. All rights reserved. | How PL/SQL uses the SGA, PGA and UGA • The SGA contains information that can be shared across schemas connected to the instance. – From the PL/SQL perspective, this is limited to package static constants. • The Process Global Area contains session-specific data that is released when the current server call terminates. – Local data • The User Global Area contains session-specific data that persists across server call boundaries – Package-level data PACKAGE Pkg is Nonstatic_Constant CONSTANT PLS_INTEGER := My_Sequence.Nextval; Static_Constant CONSTANT PLS_INTEGER := 42; END Pkg; top_pga.sql
  • 11. Copyright © 2014 Oracle and/or its affiliates. All rights reserved. | Calculating PGA and UGA Consumption • 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. show_pga_uga.sql grantv$.sql plsql_memory.pkg plsql_memory_demo.sql 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') BEGIN plsql_memory.start_analysis; run_my_application; plsql_memory.show_memory_usage; END;
  • 12. Copyright © 2014 Oracle and/or its affiliates. All rights reserved. | BULK COLLECT for multi-row querying • 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. • NO_DATA_FOUND is not raised when no rows are fetched; instead, the collection is empty. • 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. • Only integer-indexed collections may be used. • No need to initialize or extend nested tables and varrays. Done automatically by Oracle. SELECT * BULK COLLECT INTO collection(s) FROM table; FETCH cur BULK COLLECT INTO collection(s); EXECUTE IMMEDIATE query BULK COLLECT INTO collection(s);
  • 13. Copyright © 2014 Oracle and/or its affiliates. All rights reserved. | BULK COLLECT for Implicit Cursor 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 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.
  • 14. Copyright © 2014 Oracle and/or its affiliates. All rights reserved. | What's wrong with this code? • As data volume grows, PGA memory requirement grows. Eventually....KABOOM! 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;
  • 15. Copyright © 2014 Oracle and/or its affiliates. All rights reserved. | Limiting 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.
  • 16. Copyright © 2014 Oracle and/or its affiliates. All rights reserved. | 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 FETCH emps_in_dept_cur BULK COLLECT INTO emps LIMIT 1000; EXIT WHEN emps.COUNT = 0; process_emps (emps); END LOOP; CLOSE emps_in_dept_cur; END bulk_with_limit; Use the LIMIT clause with the INTO to manage the amount of memory used with the BULK COLLECT operation. Definitely the preferred approach in production applications with large or varying datasets. bulklimit.sql
  • 17. Copyright © 2014 Oracle and/or its affiliates. All rights reserved. | 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.
  • 18. Copyright © 2014 Oracle and/or its affiliates. All rights reserved. | Terminating loops containing BULK COLLECT • You will need to break the habit of checking %NOTFOUND right after the fetch. – You might skip processing some of your data. • Instead, do one of the following: – At the end of the loop, check %NOTFOUND. – Right after fetch, exit when collection.COUNT = 0. – At end of loop, exit when collection.COUNT < limit. LOOP FETCH my_cursor BULK COLLECT INTO l_collection LIMIT 100; EXIT WHEN my_cursor%NOTFOUND; BAD IDEA bulklimit_stop.sql
  • 19. Copyright © 2014 Oracle and/or its affiliates. All rights reserved. | When to convert to BULK COLLECT • Prior to Oracle10g, you should convert all multiple row fetch code to BULK COLLECTs. • On 10.1 and higher, the optimizer will automatically optimize 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. – seem to be running fast enough. • Explicit BULK COLLECTs will usually run a little faster than cursor for loops optimized to BC. 10g_optimize_cfl.sql
  • 20. Copyright © 2014 Oracle and/or its affiliates. All rights reserved. | 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. • Leave it to the optimizer to speed up "read only" cursor FOR loops.
  • 21. Copyright © 2014 Oracle and/or its affiliates. All rights reserved. | FORALL Agenda • Introduction to FORALL • Using the SQL%BULK_ROWCOUNT • Referencing fields of collections of records • Using FORALL with sparsely-filled collections • Handling errors raised during execution of FORALL
  • 22. Copyright © 2014 Oracle and/or its affiliates. All rights reserved. | Use FORALL for repeated DML operations • Convert loops that contain inserts, updates, deletes or merges to FORALL statements. • Header (usually) looks identical to a numeric FOR loop. – Implicitly declared integer iterator – At least one bind array that uses the iterator as its index value. – Use INDICES OF and VALUES OF when bind arrays are sparse 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; forall_timing.sql forall_examples.sql
  • 23. Copyright © 2014 Oracle and/or its affiliates. All rights reserved. | 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
  • 24. Copyright © 2014 Oracle and/or its affiliates. All rights reserved. | 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
  • 25. Copyright © 2014 Oracle and/or its affiliates. All rights reserved. | FORALL and collections of records • Prior to 11g, you cannot reference a field of a record in FORALL. • You must instead break data into separate collections, or... • You can also perform record-level inserts and updates. • In Oracle Database 11g, this restriction was lifted. 11g_field_of_record.sql
  • 26. Copyright © 2014 Oracle and/or its affiliates. All rights reserved. | Using FORALL with Sparse Collections • Prior to 10.2, the binding arrays in a FORALL statement must be sequentially filled. – Using the IN low .. high syntax. • Now, however, you can bind sparse collections by using INDICES OF and VALUES OF in the FORALL header. 10g_indices_of*.sql 10g_values_of*.sql 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);
  • 27. Copyright © 2014 Oracle and/or its affiliates. All rights reserved. | 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!
  • 28. Copyright © 2014 Oracle and/or its affiliates. All rights reserved. | SAVE EXCEPTIONS and FORALL • The SAVE EXCEPTIONS clause tells Oracle to save exception information and continue processing all of the DML statements. • When the FORALL statement completes, if at least one exception occurred, Oracle then raises ORA-24381. • You then check the contents of SQL%BULK_EXCEPTIONS. PROCEDURE upd_for_dept (newsal_in IN NUMBER, list_of_emps_in IN DBMS_SQL.NUMBER_TABLE) IS BEGIN FORALL indx IN list_of_emps_in.FIRST .. list_of_emps_in.LAST SAVE EXCEPTIONS UPDATE employees SET salary = newsal_in WHERE employee_id = list_of_emps_in (indx); END;
  • 29. Copyright © 2014 Oracle and/or its affiliates. All rights reserved. | Example: FORALL with SAVE EXCEPTIONS • Add SAVE EXCEPTIONS to enable FORALL to suppress errors at the statement level. CREATE OR REPLACE PROCEDURE load_books (books_in IN book_obj_list_t) IS bulk_errors EXCEPTION; PRAGMA EXCEPTION_INIT ( bulk_errors, -24381 ); BEGIN FORALL indx IN books_in.FIRST..books_in.LAST SAVE EXCEPTIONS INSERT INTO book values (books_in(indx)); EXCEPTION WHEN bulk_errors THEN FOR indx in 1..SQL%BULK_EXCEPTIONS.COUNT LOOP log_error (SQL%BULK_EXCEPTIONS(indx).ERROR_INDEX , SQL%BULK_EXCEPTIONS(indx).ERROR_CODE); END LOOP; END; Allows processing of all statements, even after an error occurs. Iterate through "pseudo- collection" of errors. bulkexc.sql If any exception is encountered, Oracle raises -24381 when done.
  • 30. Copyright © 2014 Oracle and/or its affiliates. All rights reserved. | SAVE EXCEPTIONS in Detail • For each exception raised, Oracle populates the SQL%BULK_EXCEPTIONS pseudo-collection of records. – The record has two fields : ERROR_INDEX and ERROR_CODE. – ERROR_INDEX: the index in the bind array for which the error occurred. – ERROR_CODE: the number (positive) for the error that was raised • It's a pseudo-collection and only supports a single method: COUNT. • So you iterate from 1 to SQL%BULK_EXCEPTIONS.COUNT to get information about each error. • Unfortunately, it does not store the error message.
  • 31. Copyright © 2014 Oracle and/or its affiliates. All rights reserved. | 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 • Challenges include: – With multiple DML statements in loop, how do you "communicate" from one to the other? – Avoid excessive PGA consumption
  • 32. Copyright © 2014 Oracle and/or its affiliates. All rights reserved. | 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
  • 33. Copyright © 2014 Oracle and/or its affiliates. All rights reserved. | A phased approach with bulk processing • Change from integrated, row-by-row approach to a phased approach. Relational Table(s) Relational Table Phase 1: Bulk collect from table(s) to collection Phase 3: FORALL from collection to table Phase 2: Modify contents of collection according to requirements
  • 34. Copyright © 2014 Oracle and/or its affiliates. All rights reserved. | Translating phases into code • The cfl_to_bulk_5.sql file contains the converted program, following the phased approach. cfl_to_bulk_0.sql cfl_to_bulk_5_11g.sql 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; Phase 1: Get Data Phase 3: Push Data Phase 2: Massage Data Phase 3: Push Data
  • 35. Copyright © 2014 Oracle and/or its affiliates. All rights reserved. | 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. – Look that for prime anti-pattern: Loops containing non-query DML. • 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!
  • 36. Copyright © 2014 Oracle and/or its affiliates. All rights reserved. | 36