You are on page 1of 13

Oracle PL/SQL Programming

The PL/SQL Channel


Bulk Processing in Oracle PL/SQL

Get Data Faster with


BULK COLLECT

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

Oracle PL/SQL Programming

Quick Reminders
Download code and PowerPoint documents
from www.ToadWorld.com/SF (aka, "PL/SQL
Obsession").
Make sure you are comfortable with the
material covered in the previous lesson in this
series.
Introduction to Bulk Processing
Also: make sure you know about and can work
with collections.
Copyright 2010 Feuerstein and Associates

Page 2

Oracle PL/SQL Programming

BULK COLLECT Agenda

Introduction to BULK COLLECT


Unlimited BULK COLLECTs
Using the LIMIT clause
When to convert to BULK COLLECT

Copyright 2010 Feuerstein and Associates

Page 3

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 2010 Feuerstein and Associates

Page 4

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

Page 5

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 2010 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 6

Oracle PL/SQL Programming

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 2010 Feuerstein and Associates

Page 7

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 2010 Feuerstein and Associates

Page 8

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 2010 Feuerstein and Associates

Page 9

Oracle PL/SQL Programming

Terminating loops containing BULK COLLECT


LOOP
FETCH my_cursor BULK COLLECT INTO l_collection LIMIT 100;
EXIT WHEN my_cursor%NOTFOUND;
BAD IDEA

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.
bulklimit_stop.sql
Copyright 2010 Feuerstein and Associates

Page 10

Oracle PL/SQL Programming

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 2010 Feuerstein and Associates

Page 11

Oracle PL/SQL Programming

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 2010 Feuerstein and Associates

Page 12

Oracle PL/SQL Programming

Next Steps
Download the demo.zip if you have not
already (www.ToadWorld.com/SF).
Run the sample code yourself to better
understand the features and techniques.

Watch the next lesson in this series:


Change Data Faster with FORALL

Copyright 2010 Feuerstein and Associates

Page 13

You might also like