PL/SQL has a new keyword: FORALL. This keyword tells the PL/SQL runtime engine to bulk bind into the SQL statement all the elements of one or more collections before sending anything to the SQL engine.
Although the FORALL statement contains an iteration scheme (it iterates through all the rows of a collection), it is not a FOR loop. It does not, consequently, have either a LOOP or an END LOOP statement. Its syntax is as follows:
FORALL index_row IN lower_bound ... upper_bound sql_statement ;
You must follow these rules when using FORALL:
PLS-00430: FORALL iteration variable INDX is not allowed in this context
The DML statement can reference more than one collection. The upper and lower bounds do not have to span the entire contents of the collection(s). When this statement is bulk bound and passed to SQL, the SQL engine executes the statement once for each index number in the range. In other words, the same SQL statements will be executed, but they will all be run in the same round-trip to the SQL layer, minimizing the context switches, as shown in Figure 5.3 .
Here are some examples of the use of the FORALL statement:
DECLARE TYPE not_enough_names IS VARRAY(2000) OF VARCHAR2(100); usda_inspectors not_enough_names := not_enough_names (); BEGIN -- Fill varray with the names of the US Department of Agriculture -- inspectors who are supposed to inspect over 7,000,000 businesses -- in the United States. ... -- Make government smaller: DELETE all but 100 inspectors -- and then wait for the E. coli to attack. FORALL indx IN 101 .. usda_inspectors.LAST DELETE FROM federal_employee WHERE name = usda_inspectors(indx); END;
FORALL indx IN denial.FIRST .. denial.LAST INSERT INTO hmo_coverage VALUES (denial(indx), patient_name(indx), illnesses);
The FORALL statement allows you to pass multiple SQL statements all together (in bulk) to the SQL engine. This means that as far as context switching is concerned, you have one SQL "block," but these blocks are still treated as individual DML operations.
What happens when one of those DML statements fails? The following rules apply:
First, I create a table for lobbyists of the National Rifle Association (if they are not "gun happy," then I don't know who is) and fill it with some gun information:
/* Filename on companion disk: forallerr.sql */ CREATE TABLE gun_happy ( name VARCHAR2(15), country VARCHAR2(100), killed INTEGER); INSERT INTO gun_happy VALUES('AK-47', 'Russia', 100000); INSERT INTO gun_happy VALUES('Uzi', 'Israel', 50000); INSERT INTO gun_happy VALUES('Colt-45', 'USA', 25000000);
Then I use FORALL to update the names of the guns to include the number of people killed by those guns. (Whoops! Guns don't kill people. People kill people.)
DECLARE TYPE StgList IS TABLE OF VARCHAR2(100); countries StgList := StgList ('Israel', 'Russia', 'USA'); BEGIN FORALL indx IN countries.FIRST..countries.LAST UPDATE gun_happy SET name = name || '-' || killed WHERE country = countries(indx); DBMS_OUTPUT.PUT_LINE ('Update performed!'); EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE ('Update did not complete!'); COMMIT; END; /
Take note of two things:
To see the impact of this block, I run my script with queries to show the contents of the gun_happy table:
SQL> @forallerr Gun Names --------------- AK-47 Uzi Colt-45 Use FORALL for update... Update did not complete! Gun Names --------------- AK-47-100000 Uzi-50000 Colt-45
As you can see, the first two changes stuck, whereas the last attempt to change the name failed, causing a rollback, but only to the beginning of that third UPDATE statement.
How do you know how many of your DML statements succeeded? You can check the SQL%BULK_ROWCOUNT cursor attribute; this new attribute, used exclusively with bulk binds, is discussed later in Section 5.4, "Using Cursor Attributes .
Copyright (c) 2000 O'Reilly & Associates. All rights reserved.