Bulk Collect
Bulk Collect
One method of fetching data is an Oracle bulk collect. With Oracle bulk
collect, the PL/SQL engine tells the SQL engine to collect many rows at
once and place them in a collection. During an Oracle bulk collect, the SQL
engine retrieves all the rows and loads them into the collection and switches
back to the PL/SQL engine. When rows are retrieved using Oracle bulk
collect, they are retrieved with only 2 context switches. The larger the
number of rows you would like to collect with Oracle bulk collect, the more
performance improvement you will see using an Oracle bulk collect.
Bulk collect is the bulk binding syntax for select statements. All examples
below are simplified versions of the live cases i have seen.
One of the things i usuallly come accross is that developers usually tend to
use cursor for loops to process data. They declare a cursor, open it, fetch
from it row by row in a loop and process the row they fetch.
Declare
Cursor c1 is select column_list from table_name>;
Rec1 c1%rowtype;
Begin
Open c1;
Loop;
Fetch c1 into r1;
Exit when c1%notfound;
--process rows...
End loop;
End;
Here is a simple test case to compare the performance of fetching row by
row and using bulk collect to fetch all rows into a collection.
SQL> create table t_all_objects as select * from
all_objects;
Table created.
SQL> r
1* insert into t_all_objects select * from
t_all_objects
COUNT(*)
----------
213248
SQL> declare
cursor c1 is select object_name from t_all_objects;
2 3 rec1 c1%rowtype;
4 begin
5 open c1;
6 loop
7 fetch c1 into rec1;
8 exit when c1%notfound;
9
10 null;
11
12 end loop;
13 end;
14 /
SQL> declare
2 cursor c1 is select object_name from
t_all_objects;
3 type c1_type is table of c1%rowtype;
4 rec1 c1_type;
5 begin
6 open c1;
7
8 fetch c1 bulk collect into rec1;
9
10
11 end;
12 /
Elapsed: 00:00:05.32
As can be clearly seen, bulk collecting the rows shows a huge performance
improvement over fetching row by row.
The above method (which fetched all the rows) may not be applicable to all
cases. When there are many rows to process, we can limit the number of
rows to bulk collect, process those rows and fetch again. Otherwise process
memory gets bigger and bigger as you fetch the rows.
SQL> declare
2 cursor c1 is select object_name from
t_all_objects;
3 type c1_type is table of c1%rowtype;
4 rec1 c1_type;
5 begin
6 open c1;
7 loop
8 fetch c1 bulk collect into rec1 limit 200;
9 for i in 1..rec1.count loop
10 null;
11 end loop;
12 exit when c1%notfound;
13 end loop;
14
15
16 end;
17 /
Elapsed: 00:00:04.07
When querying multiple rows of data from Oracle, don't use the cursor FOR loop.
Instead, assuming you are running at least Oracle8i, start using the wonderful, amazing
BULK COLLECT query, which improve query response time very dramatically. The
following statement, for example, retrieves all the rows in the employee table and
deposits them directly into a collection of records:
DECLARE
TYPE employee_aat IS TABLE OF employee%ROWTYPE
INDEX BY BINARY_INTEGER;
l_employees employee_aat;
BEGIN
SELECT *
BULK COLLECT INTO l_employees
FROM employee;
END;
Of course, if your table has 1,000,000 rows in it, the above block of code will consume
enormous amounts of memory. In this case, you will want to take advantage of the
LIMIT clause of BULK COLLECT as follows:
DECLARE
TYPE employee_aat IS TABLE OF employee%ROWTYPE
INDEX BY BINARY_INTEGER;
l_employees employee_aat;
Important! When you use BULK COLLECT, Oracle will not raise NO_DATA_FOUND
even if no rows are found by the implicit query. Also, within the loop (using LIMIT), you
cannot rely on cursor%FOUND to determine if the last fetch returned any rows. Instead,
check the contents of the collection. If empty, then you are done.