Implicit vs. Explicit Cursors
For a long time there have been debates over the relative merits of implicit and explicit cursors. The short answer is that implicit cursors are faster and result in much neater code so there are very few cases where you need to resort to explicit cursors.
The cursor_comparison.sql script creates a procedure that compares the performance difference between the two approaches by performing multiple queries against the dual table.
cursor_comparison.sql
CREATE OR REPLACE PROCEDURE cursor_comparison AS
l_loops NUMBER := 10000;
l_dummy dual.dummy%TYPE;
l_start NUMBER;
CREATE OR REPLACE PROCEDURE cursor_comparison AS
l_loops NUMBER := 10000;
l_dummy dual.dummy%TYPE;
l_start NUMBER;
CURSOR c_dual IS
SELECT dummy
FROM dual;
BEGIN
-- Time explicit cursor.
l_start := DBMS_UTILITY.get_time;
SELECT dummy
FROM dual;
BEGIN
-- Time explicit cursor.
l_start := DBMS_UTILITY.get_time;
FOR i IN 1 .. l_loops LOOP
OPEN c_dual;
FETCH c_dual
INTO l_dummy;
CLOSE c_dual;
END LOOP;
OPEN c_dual;
FETCH c_dual
INTO l_dummy;
CLOSE c_dual;
END LOOP;
DBMS_OUTPUT.put_line('Explicit: ' ||
(DBMS_UTILITY.get_time - l_start));
(DBMS_UTILITY.get_time - l_start));
-- Time implicit cursor.
l_start := DBMS_UTILITY.get_time;
l_start := DBMS_UTILITY.get_time;
FOR i IN 1 .. l_loops LOOP
SELECT dummy
INTO l_dummy
FROM dual;
END LOOP;
SELECT dummy
INTO l_dummy
FROM dual;
END LOOP;
DBMS_OUTPUT.put_line('Implicit: ' ||
(DBMS_UTILITY.get_time - l_start));
END cursor_comparison;
/
SHOW ERRORS
(DBMS_UTILITY.get_time - l_start));
END cursor_comparison;
/
SHOW ERRORS
The output from the procedure clearly demonstrates that implicit cursors are faster than explicit cursors.
SQL> SET SERVEROUTPUT ON
SQL> EXEC cursor_comparison;
Explicit: 203
Implicit: 162
SQL> EXEC cursor_comparison;
Explicit: 203
Implicit: 162
PL/SQL procedure successfully completed.
The interesting thing is that the implicit cursor is not only faster, but it is actually doing more work, since it includes a NO_DATA_FOUND and a TOO_MANY_ROWS exception check. To make them equivalent we should actually code the explicit cursor like that shown in the true_cursor_comparison.sql script.
true_cursor_comparison.sql
CREATE OR REPLACE PROCEDURE true_cursor_comparison AS
l_loops NUMBER := 10000;
l_dummy dual.dummy%TYPE;
l_start NUMBER;
l_loops NUMBER := 10000;
l_dummy dual.dummy%TYPE;
l_start NUMBER;
CURSOR c_dual IS
SELECT dummy
FROM dual;
BEGIN
-- Time explicit cursor.
l_start := DBMS_UTILITY.get_time;
SELECT dummy
FROM dual;
BEGIN
-- Time explicit cursor.
l_start := DBMS_UTILITY.get_time;
FOR i IN 1 .. l_loops LOOP
OPEN c_dual;
FETCH c_dual
INTO l_dummy;
OPEN c_dual;
FETCH c_dual
INTO l_dummy;
IF (c_dual%NOTFOUND) THEN
RAISE NO_DATA_FOUND;
END IF;
RAISE NO_DATA_FOUND;
END IF;
FETCH c_dual
INTO l_dummy;
IF (c_dual%FOUND) THEN
RAISE TOO_MANY_ROWS;
END IF;
CLOSE c_dual;
END LOOP;
INTO l_dummy;
IF (c_dual%FOUND) THEN
RAISE TOO_MANY_ROWS;
END IF;
CLOSE c_dual;
END LOOP;
DBMS_OUTPUT.put_line('Explicit: ' ||
(DBMS_UTILITY.get_time - l_start));
(DBMS_UTILITY.get_time - l_start));
-- Time implicit cursor.
l_start := DBMS_UTILITY.get_time;
l_start := DBMS_UTILITY.get_time;
FOR i IN 1 .. l_loops LOOP
SELECT dummy
INTO l_dummy
FROM dual;
END LOOP;
SELECT dummy
INTO l_dummy
FROM dual;
END LOOP;
DBMS_OUTPUT.put_line('Implicit: ' ||
(DBMS_UTILITY.get_time - l_start));
END true_cursor_comparison;
/
SHOW ERRORS
(DBMS_UTILITY.get_time - l_start));
END true_cursor_comparison;
/
SHOW ERRORS
The output from this procedure shows an even greater speed discrepancy.
SQL> SET SERVEROUTPUT ON
SQL> EXEC true_cursor_comparison;
Explicit: 264
Implicit: 162
SQL> EXEC true_cursor_comparison;
Explicit: 264
Implicit: 162
PL/SQL procedure successfully completed.
Since both the cursors are now doing the same amount of work why is there a speed difference? The answer is simply the volume of code being used. PL/SQL is an interpreted language so every extra line of code adds to the total processing time. As a rule of thumb, make the code as compact as possible without making it unsupportable.
One may then ask if native compilation would remove this discrepancy. That question can be answered very easily.
SQL> ALTER SESSION SET plsql_compiler_flags = 'NATIVE';
Session altered.
SQL> ALTER PROCEDURE true_cursor_comparison COMPILE;
Procedure altered.
SQL> ALTER SESSION SET plsql_compiler_flags = 'INTERPRETED';
Session altered.
SQL> SET SERVEROUTPUT ON
SQL> EXEC true_cursor_comparison;
Explicit: 263
Implicit: 160
SQL> EXEC true_cursor_comparison;
Explicit: 263
Implicit: 160
PL/SQL procedure successfully completed.
Native compilation will be dealt with in more depth later in this chapter.
This shows that there is still a speed difference between the two cursor types, so even when natively compiled the rule of "less code is faster" still holds true.
No comments:
Post a Comment