Professional Documents
Culture Documents
In this Document
Purpose
Scope and Application
Overview of Bulk Binds
Applies to:
PL/SQL - Version: 9.2.0.1 to 11.2.0.1 - Release: 9.2 to 11.2
Information in this document applies to any platform.
Purpose
The purpose of this document is to provide a quick overview of bulk binding and bulk collect. It provides information and examples of bulk operations showing how and when performance
can be improved.
Oracle Database uses two engines to run PL/SQL blocks and subprograms. The PL/SQL engine runs procedural statements, while the SQL engine runs SQL statements. During
execution, every SQL statement causes a context switch between the two engines, resulting in a performance overhead.
Performance can be improved substantially by minimizing the number of context switches required to run a particular block or subprogram. When a SQL statement runs inside a loop that
uses collection elements as bind variables, the large number of context switches required by the block can cause poor performance.
• Varrays
• Nested tables
• Index-by tables
• Host arrays
Binding is the assignment of values to PL/SQL variables in SQL statements. Bulk binding is binding an entire collection at once. Bulk binds pass the entire collection back and forth
between the two engines in a single operation.
Typically, using bulk binds improves performance for SQL statements that affect four or more database rows. The more rows affected by a SQL statement, the greater the performance
gain from bulk binds.
If you have scenarios like these in your application, consider using bulk binds to improve performance.
For example, the following PL/SQL block increases the salary for employees whose manager's ID number is 7902, 7698, or 7839, both with and without using bulk binds:
DECLARE
TYPE Numlist IS VARRAY (100) OF NUMBER;
Id NUMLIST := NUMLIST(7902, 7698, 7839);
BEGIN
Without the bulk bind, PL/SQL sends a SQL statement to the SQL engine for each employee that is updated, leading to context switches that hurt performance. If you have a set of rows
prepared in a PL/SQL table, you can bulk-insert or bulk-update the data using a loop which improves performance:
FORALL i in Emp_Data.FIRST..Emp_Data.LAST
INSERT INTO emp VALUES(Emp_Data(i));
The BULK COLLECT INTO clause can improve the performance of queries that reference collections. For example, the following PL/SQL block queries multiple values into PL/SQL tables,
both with and without bulk binds:
https://support.oracle.com/CSP/main/article?cmd=show&type=NOT&doctype=BULLETIN... 7/8/2010
Page 2 of 5
CURSOR C IS
SELECT Empno, Ename FROM emp WHERE Mgr = 7698;
BEGIN
counter := 1;
FOR rec IN C LOOP
Empno(Counter) := rec.Empno;
Ename(Counter) := rec.Ename;
Counter := Counter + 1;
END LOOP;
END;
/
You can use BULK COLLECT INTO with tables of scalar values, or tables of %TYPE values. Without the bulk bind, PL/SQL sends a SQL statement to the SQL engine for each employee
that is selected, leading to context switches that slow performance.
FOR Loops that Reference Collections and the Returning Into Clause
You can use the FORALL keyword along with the BULK COLLECT INTO keyword to improve the performance of FOR loops that reference collections and return DML. For example, the
following PL/SQL block updates the emp table by computing bonuses for a collection of employees; then it returns the bonuses in a column called bonlist. The actions are performed both
with and without using bulk binds:
DECLARE
TYPE Emplist IS VARRAY(100) OF NUMBER;
Empids EMPLIST := EMPLIST(7369, 7499, 7521, 7566, 7654, 7698);
TYPE Bonlist IS TABLE OF emp.sal%TYPE;
Bonlist_inst BONLIST := BONLIST(1,2,3,4,5);
BEGIN
FORALL i IN Empids.FIRST..Empids.LAST
UPDATE emp SET sal = 0.1 * Sal
WHERE Empno = Empids(i)
RETURNING Sal BULK COLLECT INTO Bonlist_inst;
Without the bulk bind, PL/SQL sends a SQL statement to the SQL engine for each employee that is updated, leading to context switches that hurt performance.
The most important thing to remember when you learn about and start to take advantage of features such as BULK COLLECT is that there is no free lunch. There is almost always a trade-
off to be made somewhere. The tradeoff with BULK COLLECT, like so many other performance-enhancing features, is "run faster but consume more memory."
Specifically, memory for collections is stored in the program global area (PGA), not the system global area (SGA). SGA memory is shared by all sessions connected to the Oracle
Database, but PGA memory is allocated for each session. Thus, if a program requires 5MB of memory to populate a collection and there are 100 simultaneous connections, that program
causes the consumption of 500MB of PGA memory, in addition to the memory allocated to the SGA.
Fortunately, PL/SQL makes it easy for developers to control the amount of memory used in a BULK COLLECT operation by using the LIMIT clause. Suppose I need to retrieve all the rows
from the emp table and then perform some operation on each row. I can use BULK COLLECT as follows:
Very concise, elegant, and efficient code. If, however, my emp table contains tens of thousands of rows, each of which contains hundreds of columns, this program can cause excessive
PGA memory consumption. Consequently, you should avoid this sort of "unlimited" use of BULK COLLECT. Instead, move the SELECT statement into an explicit cursor declaration and
then use a simple loop to fetch many, but not all, rows from the table with each execution of the loop body, as shown below.
https://support.oracle.com/CSP/main/article?cmd=show&type=NOT&doctype=BULLETIN... 7/8/2010
Page 3 of 5
The process_all_rows procedure given above requests that up to the value of limit_in rows be fetched at a time. PL/SQL will reuse the same limit_in elements in the collection each time
the data is fetched and thus also reuse the same memory. Even if my table grows in size, the PGA consumption will remain stable.
Scenarios in which we must go through our collection of "candidate" data for inserts and remove some (perhaps all) of the rows before doing the insert. When we try to use FORALL, we
get this error message:
ORA-22160: element at index [2750] does not exist
How can we avoid this error and get all our data inserted?
FORALL is an important enhancement to PL/SQL since Oracle8i was released. In the Oracle8i and Oracle9i Database, the only format with which you could use FORALL was this:
FORALL index_variable
IN low_value .. high_value
<DML_Statement>;
As well as a "regular" numeric FOR loop, FORALL will iterate through each integer between low_value and high_value, using that integer to identify an element in all collections that are
bound into the DML statement with the index_variable. If no element exists at a particular index value, Oracle Database raises an exception, as you can see below.
Raising ORA-22160
FORALL, in other words, requires a sequentially or densely filled collection. Now if you were still running Oracle8i or Oracle9i Database and wanted to fix this problem, you would have to
copy the data from your sparsely filled collection over to one without any gaps. From a performance standpoint, this is nothing to worry about; manipulating collections is very fast. But it
does involve writing and maintaining even more code.
In Oracle Database 10g, Oracle added two new clauses to the FORALL statement: INDICES OF and VALUES OF. They allow you to avoid the restriction on using densely filled
collections. The INDICES OF clause populates a sparse collection by using only index values that are defined. The VALUES OF clause use only index values that are found in the
elements of another defined collection.
Here is a rewrite of the code given above that avoids the ORA-22160 error.
DECLARE
TYPE list_of_names IS TABLE OF VARCHAR2 (50) INDEX BY PLS_INTEGER;
family list_of_names;
BEGIN
family (1) := 'Eli';
family (2) := 'Chris';
family (3) := 'Veva';
family (5) := 'Steven';
FORALL indx IN INDICES OF family
INSERT INTO first_names VALUES (family(indx));
END;
/
The VALUES OF clause is useful when you want to use only a subset of the collection within the DML statement. For example, below is a procedure that accepts a collection of emp
records and should insert only records for emps with a salary of $10,000 or more.
https://support.oracle.com/CSP/main/article?cmd=show&type=NOT&doctype=BULLETIN... 7/8/2010
Page 4 of 5
END insert_some;
END emp_dml;
/
The code populates a row in l_values_of with the index value from emp_in, only if the salary in that record is at least $10,000. Thus, with the FORALL statement, the VALUES OF clause
ensures that all other employee records are ignored.
The following script can be run to verify the behavior of the emp_dml package.
SELECT COUNT(*)
FROM emp
WHERE sal < 10000
/
DECLARE
l_emp emp_dml.emps;
BEGIN
SELECT * BULK COLLECT INTO l_emp
FROM emp;
emp_dml.insert_some(l_emp);
END;
/
SELECT COUNT(*)
FROM emp_copy
WHERE sal < 10000
/
Finally, you can also use the INDICES OF clause with an entirely different collection that serves as a kind of filter for the collections used in the DML statement. Code provided below
shows an example of this approach.
SQL> select sal, empno, ename from emp_copy where empno in (7369, 7499, 7521);
--Run the following code to set the salary of Smith and Ward to 10000
DECLARE
TYPE emps IS TABLE OF emp_copy.empno%TYPE INDEX BY PLS_INTEGER;
l_emps emps;
TYPE boolean_emp IS TABLE OF Boolean INDEX BY PLS_INTEGER;
l_emps_index boolean_emp;
BEGIN
l_emps (1) := 7369;
l_emps (100) := 7499;
l_emps (500) := 7521;
SQL> select sal, empno, ename from emp_copy where empno in (7369, 7499, 7521);
This code uses the index values of defined elements in the l_emp_index collection to specify which elements of the l_emps collection to use in the update statement. Note that a
BETWEEN clause is added to constrain which of the index values of l_emp_index will be used.
The PLS-00436 restriction has been removed from 11g. Each element of a collection object can now be referenced using SET and WHERE clauses of a DML statement in a FORALL
construct.
Related
Products
• Oracle Database Products > Oracle Database > Application Development > PL/SQL
Keywords
FORALL
Errors
https://support.oracle.com/CSP/main/article?cmd=show&type=NOT&doctype=BULLETIN... 7/8/2010
Page 5 of 5
Back to top
https://support.oracle.com/CSP/main/article?cmd=show&type=NOT&doctype=BULLETIN... 7/8/2010