Today I stumbled upon a case where I would readily assume a simple optimization
as a no-brainer.
Suppose you have a table called my_table with a column named my_column and
you write a stored procedure like this:
procedure my_proc(a_value my_table.my_column%type := null) is
begin
for i in (
select another_column from my_table
where a_value is null or my_column = a_value)
loop
-- do something
end loop;
end;
I’ve always assumed that the expression a_value is null is constant for the
sake of the select statement, or any expression consisting purely of PL variables and other constants for that
matter. In other words, it could safely be evaluated prior to the execution of
the query and substituted for a constant. In this code, for example, when
a_value is not passed, the query would be equivalent to
select another_column from my_table
Conversely, when the value is passed, the query would be equivalent to
select another_column from my_table
where my_column = a_value
For my surprise, this simple optimization is not made. The a_value is null
expression seems to get executed for every record on the table and, with a
sufficiently large table, the difference is noticeable even without any special
tools. I am using version 10 and would regard this optimization as a 2.0 or 3.0
feature that would have been done a long time ago.
There must be some reason for this, obviously. Maybe my assumption that PL
variables are constants to the eyes of the SQL query is not true. Maybe PL
variables can change during the execution of SQL queries. Do you know of any
such case?
When Oracle needs to compile and optimize the SQL query it must create a query plan that will work no matter what the values of the bind variables will be, because that plan can be reused with the same query but different values later.
In the query,
select another_column from my_table where a_value is null or my_column = a_valuea_value is the bind variable. The query will only hard parsed into a query plan once. It can not collapse toselect another_column from my_tablesince the next call to the stored procedure may pass in a non-null a_value.EDIT Adding an example.
Ask Tom has a posting that deals with a more complicated version of this issue. Based on his answer.
Sample data:
The procedure:
Watch it run: