BULK COLLECT allows you to retrieve multiple rows of data directly into PL/SQL Collections. It will raise NO_DATA_FOUND if it doesn't find any rows, but it certainly doesn't raise TOO_MANY_ROWS if it finds more than one!
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 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 employees table and then perform some compensation analysis on each row. I can use BULK COLLECT as follows:
PROCEDURE process_all_rows
IS
TYPE employees_aat
IS TABLE OF employees%ROWTYPE
INDEX BY PLS_INTEGER;
l_employees employees_aat;
BEGIN
SELECT *
BULK COLLECT INTO l_employees
FROM employees;
FOR indx IN 1 .. l_employees.COUNT
LOOP
analyze_compensation
(l_employees(indx));
END LOOP;
END process_all_rows;
Very concise, elegant, and efficient code. If, however, my employees 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,
Using BULK COLLECT with LIMIT clause
PROCEDURE process_all_rows (limit_in IN PLS_INTEGER DEFAULT 100) IS CURSOR employees_cur IS SELECT * FROM employees; TYPE employees_aat IS TABLE OF employees_cur%ROWTYPE INDEX BY PLS_INTEGER; l_employees employees_aat; BEGIN OPEN employees_cur; LOOP FETCH employees_cur BULK COLLECT INTO l_employees LIMIT limit_in; EXIT WHEN table_with_227_rows_cur%NOTFOUND; FOR indx IN 1 .. l_employees.COUNT LOOP analyze_compensation (l_employees(indx)); END LOOP; EXIT WHEN l_employees.COUNT < limit_in; END LOOP; CLOSE employees_cur; END process_all_rows;
No comments:
Post a Comment