The importance of collections
Door Dennis
Introduction
Today I will be addressing some cool features in PL/SQL that are often not taken advantage of.
Even to this day I often see people write code without using collections, even though they exist already for a long time. Many people learned PL/SQL and moved on with their current knowledge. But time does not stand still and many things have been optimized and changed in the Oracle Database.
Cursor problem
One of the things I (unfortunately) come across too often is creating cursors, explicitly or implicitly. The main rule is to avoid keeping them opened for a long time. Opening and opened cursors when not necessary affects general performance, opened cursors use memory and fetching from them requires I/O as Oracle needs to read data blocks on disk. Nowadays, Oracle is pretty good at creating good optimization plans based on correct statistics, but it can still heavily impact your performance.
Using collections as an alternative
As stated in my previous blogpost, using collections is much faster than writing cursor for loops as it prevents context switching. It also reads more in-memory data instead of disk data. This means it reduces I/O.
Here are 2 examples. The first one uses a "classic" for loop, the second uses a bulk operation.
DECLARE
CURSOR c_emps
IS
SELECT e.ename employee, e.job, m.ename manager,
d.dname department, d.loc location
FROM emp e, emp m, dept d
WHERE e.deptno = d.deptno
AND m.mgr(+) = e.empno
ORDER BY MANAGER NULLS FIRST
;
PROCEDURE p(s IN VARCHAR2)
IS
BEGIN
dbms_output.put_line(s);
END;
PROCEDURE ph(s IN VARCHAR2) -- fancy heading
IS
BEGIN
dbms_output.put_line(chr(13)||chr(10));
dbms_output.put_line(rpad('-',length(s)+6,'-'));
dbms_output.put_line('-- '||s||' --');
dbms_output.put_line(rpad('-',length(s)+6,'-'));
dbms_output.put_line(chr(13)||chr(10));
END;
BEGIN
ph('Using "classic" cursor for loop');
FOR r_emps IN c_emps
LOOP
p(r_emps.employee||'|'
||r_emps.job||'|'
||r_emps.manager||'|'
||r_emps.department||'|'
||r_emps.location);
END LOOP; -- cursor gets closed after the loop
END;
/
Example 2.1 - using a "classic" cursor for loop
DECLARE
CURSOR c_emps
IS
SELECT e.ename employee, e.job, m.ename manager,
d.dname department, d.loc location
FROM emp e, emp m, dept d
WHERE e.deptno = d.deptno
AND m.mgr(+) = e.empno
ORDER BY MANAGER NULLS FIRST
;
TYPE t_emps_tab IS TABLE OF c_emps%ROWTYPE
INDEX BY PLS_INTEGER;
v_emps_tab t_emps_tab;
PROCEDURE p(s IN VARCHAR2)
IS
BEGIN
dbms_output.put_line(s);
END;
PROCEDURE ph(s IN VARCHAR2) -- fancy heading
IS
BEGIN
dbms_output.put_line(chr(13)||chr(10));
dbms_output.put_line(rpad('-',length(s)+6,'-'));
dbms_output.put_line('-- '||s||' --');
dbms_output.put_line(rpad('-',length(s)+6,'-'));
dbms_output.put_line(chr(13)||chr(10));
END;
BEGIN
ph('using bulk and collection');
OPEN c_emps;
FETCH c_emps BULK COLLECT INTO v_emps_tab;
CLOSE c_emps; -- note that the cursor is closed before processing the data
FOR i IN v_emps_tab.first .. v_emps_tab.last
LOOP
p(v_emps_tab(i).employee||'|'
||v_emps_tab(i).job||'|'
||v_emps_tab(i).manager||'|'
||v_emps_tab(i).department||'|'
||v_emps_tab(i).location||'|');
END LOOP;
END;
/