Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Programming PL SQL.doc
Скачиваний:
3
Добавлен:
01.07.2025
Размер:
5.06 Mб
Скачать

13.2.2 Context-Switching Problem Scenarios

Before we look at the details of FORALL, let's examine the scenarios in which excessive context switches are likely to cause problems. These scenarios may occur when you are processing multiple rows of information stored (or to be deposited) in a collection (a VARRAY, nested table, or associative array).

Suppose, for example, that I need to write a procedure to update the page count of specified books in my books table. Inputs to my program are two collections, one that contains the ISBN numbers of the books to be updated and another that holds the page counts for those books. Here's the solution I would have written prior to Oracle 8.1 (taking advantage of two previously defined variable array types, name_varray and number_varray):

CREATE OR REPLACE PROCEDURE order_books (

isbn_in IN name_varray,

new_count_in IN number_varray)

IS

BEGIN

FOR indx IN isbn_in.FIRST .. isbn_in.LAST

LOOP

UPDATE books

SET page_count = new_count_in (indx)

WHERE isbn = isbn_in (indx);

END LOOP;

END;

If I needed to insert 100 rows, then I would be performing at least 100 context switches, because each update is processed in a separate trip to the SQL engine. Figure 13-3 illustrates this excessive (but previously unavoidable) switching.

Figure 13-3. Excessive context switching for multiple updatEs

You can also run into lots of switching when you fetch multiple rows of information from a cursor into a collection. Here is an example of the kind of code that cries out for the bulk collection feature:

DECLARE

CURSOR major_polluters IS

SELECT name, mileage

FROM cars_and_trucks

WHERE vehicle_type IN ('SUV', 'PICKUP');

names name_varray := name_varray( );

mileages number_varray := number_varray( );

BEGIN

FOR bad_car IN major_polluters

LOOP

names.EXTEND;

names (major_polluters%ROWCOUNT) := bad_car.name;

mileages.EXTEND;

mileages (major_polluters%ROWCOUNT) := bad_car.mileage;

END LOOP;

... now work with data in the arrays ...

END;

If you find yourself writing code like either of these examples, you will be much better off switching to one of the bulk operations explored in the following sections. In particular, you should keep an eye out for these cues in your code:

  • A recurring SQL statement inside a PL/SQL loop. (It doesn't have to be a FOR loop, but that is the most likely candidate.)

  • Some parameter that can be made a bind variable. You need to be able to load those values into a collection to have it processed by FORALL.

13.2.3 Forall Examples

Here are some examples of the use of the FORALL statement:

  • Let's rewrite the order_books procedure to use FORALL:

  • CREATE OR REPLACE PROCEDURE order_books (

  • isbn_in IN name_varray,

  • new_count_in IN number_varray)

  • IS

  • BEGIN

  • FORALL indx IN isbn_in.FIRST .. isbn_in.LAST

  • UPDATE books

  • SET page_count = new_count_in (indx)

  • WHERE isbn = isbn_in (indx);

END;

Notice that the only changes in this example are to change FOR to FORALL, and to remove the LOOP and END LOOP keywords. This use of FORALL accesses and passes to SQL each of the rows defined in the two collections. Figure 13-2 shows the change in behavior that results.

  • The next example shows how the DML statement can reference more than one collection. In this case, I have three collections: denial, patient_name, and illnesses. Only the first two are subscripted, and so individual elements of the collection are passed to each INSERT. The third column in health_coverage is a collection listing preconditions. Because the PL/SQL engine bulk binds only subscripted collections, the illnesses collection is placed in that column for each row inserted:

  • FORALL indx IN denial.FIRST .. denial.LAST

  • INSERT INTO health_coverage

VALUES (denial(indx), patient_name(indx), illnesses);

  • Use the RETURNING clause in a FORALL statement to retrieve information about each separate DELETE statement. Notice that the RETURNING clause in FORALL must use BULK COLLECT INTO (the corresponding "bulk" operation for queries):

  • CREATE OR REPLACE FUNCTION remove_emps_by_dept (deptlist dlist_t)

  • RETURN enolist_t

  • IS

  • enolist enolist_t;

  • BEGIN

  • FORALL aDept IN deptlist.FIRST..deptlist.LAST

  • DELETE FROM emp WHERE deptno IN deptlist(aDept)

  • RETURNING empno BULK COLLECT INTO enolist;

  • RETURN enolist;

END;

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]