The PL/SQL optimizer performs the optimization techniques based on the level value that is set for the PLSQL_OPTIMIZE_LEVEL parameter. The possible values allowed for this parameter are 0, 1, 2 (Default), 3 (11g and higher only). The higher the value set for this parameter, the more the effort the PL/SQL optimizer makes to optimize the PL/SQL units.
The prototype of the PLSQL_OPTIMIZE_LEVEL initialization parameter is shown below,
plsql_optimize_level=<0|1|2|3>;
Where, 2 is the default.
Altering the Optimization Level
The PL/SQL optimization level can be set/altered using the PLSQL_OPTIMIZE_LEVEL initialization parameter either at the SYSTEM or SESSION or at the OBJECT level.
The prototype for setting the optimization level either at the SYSTEM or SESSION level is shown below,
ALTER <SYSTEM | SESSION> SET plsql_optimize_level=<0|1|2|3>;
The prototype for setting the optimization at the object level is shown below,
ALTER <object_type> <object_name> COMPILE plsql_optimize_level = <0|1|2|3>;
If the object is recompiled without the REUSE SETTINGS clause, it takes the database’s current optimization level as its own. With this clause included in the compilation script, the object retains the optimization level it was last compiled with, instead of the database’s current optimization level.
Consider the test procedure for the further demonstration of the PLSQL_OPTIMIZE_LEVEL parameter for the implicit conversion of LOOPs into BULK FETCHes.
CREATE OR replace PROCEDURE Proc_test
authid current_user
IS
l_pi_start_time PLS_INTEGER;
l_pi_end_time PLS_INTEGER;
l_pi_cpu_start_time PLS_INTEGER;
l_pi_cpu_end_time PLS_INTEGER;
l_pi_var1 PLS_INTEGER;
l_pi_var2 PLS_INTEGER;
FUNCTION Func_test( ip_pi_var1 PLS_INTEGER)
RETURN PLS_INTEGER
IS
BEGIN
RETURN ip_pi_var1+ip_pi_var1;
END;
BEGIN
l_pi_start_time :=NULL;
l_pi_cpu_start_time :=NULL;
l_pi_start_time :=dbms_utility.get_time;
l_pi_cpu_start_time :=dbms_utility.get_cpu_time;
FOR i IN
(
SELECT object_name
FROM dba_objects )
LOOP
NULL;
END LOOP i;
FOR j IN
(
SELECT table_name,
column_name
FROM dba_tab_cols )
LOOP
l_pi_var2 :=0;
l_pi_var2 :=1;
END LOOP j;
l_pi_var2 :=Func_test(10);
IF 1 =1 THEN
l_pi_var2 :=100;
ELSE
l_pi_var2:=1000;
END IF;
NULL;
l_pi_end_time :=dbms_utility.get_time;
l_pi_cpu_end_time :=dbms_utility.get_cpu_time;
dbms_output.put_line( 'Program run time: '
||(l_pi_end_time l_pi_start_time) /100
||' Seconds');
dbms_output.put_line( 'CPU run time: '
||(l_pi_cpu_end_time l_pi_cpu_start_time)/100
||' Seconds');
END;
/
authid current_user
IS
l_pi_start_time PLS_INTEGER;
l_pi_end_time PLS_INTEGER;
l_pi_cpu_start_time PLS_INTEGER;
l_pi_cpu_end_time PLS_INTEGER;
l_pi_var1 PLS_INTEGER;
l_pi_var2 PLS_INTEGER;
FUNCTION Func_test( ip_pi_var1 PLS_INTEGER)
RETURN PLS_INTEGER
IS
BEGIN
RETURN ip_pi_var1+ip_pi_var1;
END;
BEGIN
l_pi_start_time :=NULL;
l_pi_cpu_start_time :=NULL;
l_pi_start_time :=dbms_utility.get_time;
l_pi_cpu_start_time :=dbms_utility.get_cpu_time;
FOR i IN
(
SELECT object_name
FROM dba_objects )
LOOP
NULL;
END LOOP i;
FOR j IN
(
SELECT table_name,
column_name
FROM dba_tab_cols )
LOOP
l_pi_var2 :=0;
l_pi_var2 :=1;
END LOOP j;
l_pi_var2 :=Func_test(10);
IF 1 =1 THEN
l_pi_var2 :=100;
ELSE
l_pi_var2:=1000;
END IF;
NULL;
l_pi_end_time :=dbms_utility.get_time;
l_pi_cpu_end_time :=dbms_utility.get_cpu_time;
dbms_output.put_line( 'Program run time: '
||(l_pi_end_time l_pi_start_time) /100
||' Seconds');
dbms_output.put_line( 'CPU run time: '
||(l_pi_cpu_end_time l_pi_cpu_start_time)/100
||' Seconds');
END;
/
After the procedure is created, its compilation mode can be verified by querying the USER_PLSQL_OBJECT_SETTINGS data dictionary table as shown below,
SELECT name,
TYPE,
plsql_optimize_level
FROM user_plsql_object_settings
WHERE name = 'PROC_TEST';
TYPE,
plsql_optimize_level
FROM user_plsql_object_settings
WHERE name = 'PROC_TEST';
NAME TYPE PLSQL_OPTIMIZE_LEVEL
---------------- ------------------ ------------------------------------
PROC_TEST PROCEDURE 2
Note: The default optimization level for the procedure will be the database’s current optimization level.
The PLSQL_OPTIMIZE_LEVEL=2 setting performs the below implicit actions on the package.
- Redundant code removal
- The null assignment to the variables, L_PI_START_TIME and L_PI_CPU_START_TIME in the line numbers 16 and 17 respectively, are already null during their creation in the declaration section, thus making these two statements redundant.
- The statements L_PI_VAR2=0 and L_PI_VAR2=1 are redundant as it assigns a literal integer for every iteration inside the loop. Also, the assignment of the first statement is overridden by the second statement. The PL/SQL compiler finds these properties, takes the first statement (L_PI_VAR2=0) out of the loop structure and skips the second statement (L_PI_VAR2=1) during the program execution.
- Unreachable code removal
- The statement l_pi_var2:=1000; is unreachable in the code and this will be avoided.
- Implicit conversion of Cursor FOR LOOP into BULK ARRAY FETCH.
- This setting along with NCOMP mode (PLSQL_CODE_TYPE=NATIVE) converts all the FOR loops into ARRAY fetches with a limit of 100. let us compare the result of the trace file output for the above package with PLSQL_OPTIMIZE_LEVEL>=2 and PLSQL_OPTIMIZE_LEVEL<=1 settings respectively.
The above trace file output is extracted when the procedure code was executed with the PLSQL_OPTIMIZE_LEVEL parameter value set to <=1. The total number of rows processed is 2,24,208 and the total fetch operations performed is 2,24,214. This indicates that approximately 1 row is fetched for each fetch operation, resulting in CPU overhead and context switching.
The above trace file output is extracted when the procedure code was executed with the PLSQL_OPTIMIZE_LEVEL parameter value set to >=2 in the NCOMP mode (PLSQL_CODE_TYPE=NATIVE). The total number of rows processed is 2,24,208 and the total fetches performed is 2,247. This shows that the recursive statements are implicitly converted into ARRAY fetches with a limit of 100.
This approach automatically reduces the context switching and increases the performance of the package.
Thank you,
Boobal Ganesan
Author of Advanced PL/SQL Programming - The Definitive Reference