Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
50
Добавлен:
16.04.2013
Размер:
5.97 Mб
Скачать

Reducing Loop Overhead for DML Statements and Queries (FORALL, BULK COLLECT)

error-reporting function SQLERRM, which expects a negative number. Here is the output:

Number of errors is 3

Error 1 occurred during iteration 2

Oracle error is ORA-01476: divisor is equal to zero

Error 2 occurred during iteration 6

Oracle error is ORA-01476: divisor is equal to zero

Error 3 occurred during iteration 10

Oracle error is ORA-01476: divisor is equal to zero

Retrieving Query Results into Collections with the BULK COLLECT Clause

Using the keywords BULK COLLECT with a query is a very efficient way to retrieve the result set. Instead of looping through each row, you store the results in one or more collections, in a single operation. You can use these keywords in the SELECT INTO and FETCH INTO statements, and the RETURNING INTO clause.

With the BULK COLLECT clause, all the variables in the INTO list must be collections. The table columns can hold scalar or composite values, including object types. The following example loads two entire database columns into nested tables:

DECLARE

TYPE NumTab IS TABLE OF employees.employee_id%TYPE;

TYPE NameTab IS TABLE OF employees.last_name%TYPE;

enums

NumTab;

--

No need to initialize

the collections.

names

NameTab;

--

Values will be filled

in by the SELECT INTO.

PROCEDURE print_results IS

 

BEGIN dbms_output.put_line('Results:'); FOR i IN enums.FIRST .. enums.LAST LOOP

dbms_output.put_line(' Employee #' || enums(i) || ': ' || names(i));

END LOOP; END;

BEGIN

SELECT employee_id, last_name -- Retrieve data for 10 arbitrary employees. BULK COLLECT INTO enums, names

FROM employees WHERE ROWNUM < 11;

--The data has all been brought into memory by BULK COLLECT.

--No need to FETCH each row from the result set. print_results;

SELECT employee_id, last_name -- Retrieve approximately 20% of all rows

BULK COLLECT INTO enums, names FROM employees SAMPLE (20);

print_results; END;

/

The collections are initialized automatically. Nested tables and associative arrays are extended to hold as many elements as needed. If you use varrays, all the return values must fit in the varray's declared size. Elements are inserted starting at index 1, overwriting any existing elements.

Since the processing of the BULK COLLECT INTO clause is similar to a FETCH loop, it does not raise a NO_DATA_FOUND exception if no rows match the query. You must check whether the resulting nested table or varray is null, or if the resulting associative array has no elements.

Tuning PL/SQL Applications for Performance 11-15

Reducing Loop Overhead for DML Statements and Queries (FORALL, BULK COLLECT)

To prevent the resulting collections from expanding without limit, you can use the pseudocolumn ROWNUM to limit the number of rows processed. Or, you can use the SAMPLE clause to retrieve a random sample of rows.

DECLARE

TYPE SalList IS TABLE OF emp.sal%TYPE; sals SalList;

BEGIN

--Limit the number of rows to 100.

SELECT sal BULK COLLECT INTO sals FROM emp WHERE ROWNUM <= 100;

--Retrieve 10% (approximately) of the rows in the table. SELECT sal BULK COLLECT INTO sals FROM emp SAMPLE 10;

END;

/

You can process very large result sets by fetching a specified number of rows at a time from a cursor, as shown in the following sections.

Examples of Bulk-Fetching from a Cursor

Example 11–6 Bulk-Fetching from a Cursor Into One or More Collections

You can fetch from a cursor into one or more collections:

DECLARE

TYPE NameList IS TABLE OF employees.last_name%TYPE; TYPE SalList IS TABLE OF employees.salary%TYPE;

CURSOR c1 IS SELECT last_name, salary FROM employees WHERE salary > 10000; names NameList;

sals SalList;

TYPE RecList IS TABLE OF c1%ROWTYPE; recs RecList;

PROCEDURE print_results IS BEGIN

dbms_output.put_line('Results:');

IF names IS NULL OR names.COUNT = 0 THEN

RETURN; -- Don't print anything if collections are empty. END IF;

FOR i IN names.FIRST .. names.LAST LOOP

dbms_output.put_line(' Employee ' || names(i) || ': $' || sals(i));

END LOOP; END;

BEGIN

dbms_output.put_line('--- Processing all results at once ---'); OPEN c1;

FETCH c1 BULK COLLECT INTO names, sals; CLOSE c1;

print_results;

dbms_output.put_line('--- Processing 7 rows at a time ---'); OPEN c1;

LOOP

FETCH c1 BULK COLLECT INTO names, sals LIMIT 7; EXIT WHEN c1%NOTFOUND;

print_results;

11-16 PL/SQL User's Guide and Reference

Reducing Loop Overhead for DML Statements and Queries (FORALL, BULK COLLECT)

END LOOP;

--Loop exits when fewer than 7 rows are fetched. Have to

--process the last few. Need extra checking inside PRINT_RESULTS

--in case it is called when the collection is empty. print_results;

CLOSE c1;

dbms_output.put_line('--- Fetching records rather than columns ---'); OPEN c1;

FETCH c1 BULK COLLECT INTO recs; FOR i IN recs.FIRST .. recs.LAST LOOP

--Now all the columns from the result set come from a single record. dbms_output.put_line(' Employee ' || recs(i).last_name || ': $'

||recs(i).salary);

END LOOP; END;

/

Example 11–7 Bulk-Fetching from a Cursor Into a Collection of Records

You can fetch from a cursor into a collection of records:

DECLARE

TYPE DeptRecTab IS TABLE OF dept%ROWTYPE; dept_recs DeptRecTab;

CURSOR c1 IS

SELECT deptno, dname, loc FROM dept WHERE deptno > 10;

BEGIN

OPEN c1;

FETCH c1 BULK COLLECT INTO dept_recs; END;

/

Limiting the Rows for a Bulk FETCH Operation with the LIMIT Clause

The optional LIMIT clause, allowed only in bulk FETCH statements, limits the number of rows fetched from the database.

In the example below, with each iteration of the loop, the FETCH statement fetches ten rows (or less) into index-by table empnos. The previous values are overwritten.

DECLARE

TYPE NumTab IS TABLE OF NUMBER INDEX BY BINARY_INTEGER; CURSOR c1 IS SELECT empno FROM emp;

empnos NumTab;

rows NATURAL := 10; BEGIN

OPEN c1; LOOP

/* The following statement fetches 10 rows (or less). */ FETCH c1 BULK COLLECT INTO empnos LIMIT rows;

EXIT WHEN c1%NOTFOUND;

...

END LOOP; CLOSE c1;

END;

/

Tuning PL/SQL Applications for Performance 11-17

Reducing Loop Overhead for DML Statements and Queries (FORALL, BULK COLLECT)

Retrieving DML Results into a Collection with the RETURNING INTO Clause

You can use the BULK COLLECT clause in the RETURNING INTO clause of an INSERT, UPDATE, or DELETE statement:

CREATE TABLE emp2 AS SELECT * FROM employees; DECLARE

TYPE NumList IS TABLE OF employees.employee_id%TYPE; enums NumList;

TYPE NameList IS TABLE OF employees.last_name%TYPE; names NameList;

BEGIN

DELETE FROM emp2 WHERE department_id = 30

RETURNING employee_id, last_name BULK COLLECT INTO enums, names; dbms_output.put_line('Deleted ' || SQL%ROWCOUNT || ' rows:');

FOR i IN enums.FIRST .. enums.LAST LOOP

dbms_output.put_line('Employee #' || enums(i) || ': ' || names(i)); END LOOP;

END;

/

DROP TABLE emp2;

Using FORALL and BULK COLLECT Together

You can combine the BULK COLLECT clause with a FORALL statement. The output collections are built up as the FORALL statement iterates.

In the following example, the EMPNO value of each deleted row is stored in the collection ENUMS. The collection DEPTS has 3 elements, so the FORALL statement iterates 3 times. If each DELETE issued by the FORALL statement deletes 5 rows, then the collection ENUMS, which stores values from the deleted rows, has 15 elements when the statement completes:

CREATE TABLE emp2 AS SELECT * FROM employees; DECLARE

TYPE NumList IS TABLE OF NUMBER; depts NumList := NumList(10,20,30);

TYPE enum_t IS TABLE OF employees.employee_id%TYPE; TYPE dept_t IS TABLE OF employees.department_id%TYPE; e_ids enum_t;

d_ids dept_t; BEGIN

FORALL j IN depts.FIRST..depts.LAST

DELETE FROM emp2 WHERE department_id = depts(j)

RETURNING employee_id, department_id BULK COLLECT INTO e_ids, d_ids; dbms_output.put_line('Deleted ' || SQL%ROWCOUNT || ' rows:');

FOR i IN e_ids.FIRST .. e_ids.LAST LOOP

dbms_output.put_line('Employee #' || e_ids(i) || ' from dept #' || d_ids(i));

END LOOP; END;

/

DROP TABLE emp2;

The column values returned by each execution are added to the values returned previously. If you use a FOR loop instead of the FORALL statement, the set of returned values is overwritten by each DELETE statement.

You cannot use the SELECT ... BULK COLLECT statement in a FORALL statement.

11-18 PL/SQL User's Guide and Reference

Соседние файлы в папке Oracle 10g