Caution when using PL/SQL functions in a SQL statement
Be cautious if you use PL/SQL functions/packages in your SQL statements, especially in the Where-Clause!
Why?
Because the function is probably called more often than you think it is! If you don't want to read the hole article, scroll down and check the APEX impact.
An example
Let's create the following simple function.
If you have bet your money on For each row which is checked by the query engine, then you are the winner! It's called 107 times! As often as we have rows in the table.
If we change the query to
Check the DBMS_Output.
It's just called one time! What is the difference? In our second example the optimizer is using the EMP_EMP_ID_PK index and in that case as Tom Kyte explained me
So what's the deal all about it?
Keep in mind the performance issue when you have a function which is selecting against other tables and you use such a function in a where-clause, where 1.000's of records are processed!
Possible solutions?
You can change the query in the following way. The optimizer then knows that is ok when he executes the query just one time. So called scalar subquery caching, which works with all DB versions. Checkout AskTom about that term.
So only if you have a 10.2.x database, using DETERMINISTIC will give you a speed up of your query. Use the above solution instead.
But let's change our function and see what we get now.
Try the following SQL statement.
I know that the Oracle documentation about DETERMINISTIC says
Which I think is basically to see in relation to the function based index for which this "hint" has been introduced. From my observation, if you have such a function call in several SQL statements which are called after each other (eg in a anonymous pl/sql block), the function is executed again for each SQL statement. It's just a hint for the optimizer during execution of the current statement.
DON'T USE DETERMINISTIC if your function is changing some global variables (eg increase a counter) or doing DML and you expect that the function is called for each row!
And how is that all related to APEX?
Are you using the V or NV function in your SQL statements? In APEX 2.0 the V function isn't wrapped (in 2.2 it is) and I don't see that the DETERMINISTIC is used in the code... It's just memory processing they do in there, but if you call it for 1.000's of records...
Always use bind variables instead! Only use the V function in your packages, but never use them in your report SQL statements.
I have done a posting on OTN about speeding up the V function. Stay tuned.
UPDATE: See my posting Drop in replacement for V and NV function which contains a wrapper for the existing functions.
Why?
Because the function is probably called more often than you think it is! If you don't want to read the hole article, scroll down and check the APEX impact.
An example
Let's create the following simple function.
CREATE OR REPLACE FUNCTION getValueAnd now look at the following SQL Statement where we call our new function with a constant value.
( pValue IN NUMBER
)
RETURN NUMBER
IS
BEGIN
DBMS_Output.put_line
( 'called getValue with '||pValue||' at '||
TO_CHAR(SYSDATE, 'HH24:MI:SS')
);
RETURN pValue;
END getValue;
SELECT COUNT(*)How often do you think is the function called?
FROM EMPLOYEES
WHERE SALARY = getValue(1)
;
- One time because it always uses the same value as parameter?
- For each row which is checked by the query engine?
- Some number between one time and for all rows?
If you have bet your money on For each row which is checked by the query engine, then you are the winner! It's called 107 times! As often as we have rows in the table.
If we change the query to
SELECT COUNT(*)How often to you think it is called? You have the same choice as before.
FROM EMPLOYEES
WHERE EMPLOYEE_ID > getValue(1)
;
Check the DBMS_Output.
It's just called one time! What is the difference? In our second example the optimizer is using the EMP_EMP_ID_PK index and in that case as Tom Kyte explained me
The optimizer gets the start/stop range (the function might be called twice actually) and then it range scans with the constants.
So what's the deal all about it?
Keep in mind the performance issue when you have a function which is selecting against other tables and you use such a function in a where-clause, where 1.000's of records are processed!
Possible solutions?
You can change the query in the following way. The optimizer then knows that is ok when he executes the query just one time. So called scalar subquery caching, which works with all DB versions. Checkout AskTom about that term.
SELECT COUNT(*)Or you take a look at the DETERMINISTIC clause which can specified when creating a function or a package functions. What does it do? It's a hint for the optimizer to indicate that the function returns the same result value whenever it is called with the same values for its arguments. But, as noted by Tom Kyte
FROM EMPLOYEES
WHERE SALARY = (SELECT getValue(1) FROM DUAL)
;
10gr2 supports deterministic as an optimization in SQL for the first time. Prior to 10gr2 - deterministic was all about function based indexes only - the SQL engine ignored it.
So only if you have a 10.2.x database, using DETERMINISTIC will give you a speed up of your query. Use the above solution instead.
But let's change our function and see what we get now.
CREATE OR REPLACE FUNCTION getValueRun the first query again and you will see that the function is called just once!
( pValue IN NUMBER
)
RETURN NUMBER DETERMINISTIC
IS
BEGIN
DBMS_Output.put_line
( 'called getValue with '||pValue||' at '||
TO_CHAR(SYSDATE, 'HH24:MI:SS')
);
RETURN pValue;
END getValue;
Try the following SQL statement.
SELECT COUNT(*)How often do you think it is called? It's just called as often as there are different salaries in the table. With the old version of the function is called for each row again.
FROM EMPLOYEES
WHERE SALARY = getValue(SALARY)
;
I know that the Oracle documentation about DETERMINISTIC says
Do not specify this clause to define a function that uses package variables or that accesses the database in any way that might affect the return result of the function.
Which I think is basically to see in relation to the function based index for which this "hint" has been introduced. From my observation, if you have such a function call in several SQL statements which are called after each other (eg in a anonymous pl/sql block), the function is executed again for each SQL statement. It's just a hint for the optimizer during execution of the current statement.
DON'T USE DETERMINISTIC if your function is changing some global variables (eg increase a counter) or doing DML and you expect that the function is called for each row!
And how is that all related to APEX?
Are you using the V or NV function in your SQL statements? In APEX 2.0 the V function isn't wrapped (in 2.2 it is) and I don't see that the DETERMINISTIC is used in the code... It's just memory processing they do in there, but if you call it for 1.000's of records...
Always use bind variables instead! Only use the V function in your packages, but never use them in your report SQL statements.
I have done a posting on OTN about speeding up the V function. Stay tuned.
UPDATE: See my posting Drop in replacement for V and NV function which contains a wrapper for the existing functions.
Labels: caution, performance, SQL, tips
















3 Comments:
As I said in thread, our problems was basically based on parameter views (where somecol=v('SOME_ITEM')
So in such a cases "v" is almost inevitable....but issue is the sameinevitable (http://forums.oracle.com/forums/post!reply.jspa?messageID=1558784)
By
Damir, at 01 December, 2006 08:13
Hi Damir,
for a table with just a few rows it's probably no problem, but there are always tables which have more rows in them and developers tend to use "design patterns" they have already used in the past. And in that case you have bad performance.
I think in such a case it's better to already come up with a more performant solution, also for the small tables. To have a better "design pattern" for the future, where you don't have to care anymore.
I think using the APEX feature "Application Attributes/Edit Security Attributes/Virtual Private Database" is a much better approach. Setting the context with DBMS_Session.set_context and using SYS_CONTEXT in the view definition is much better. I can not prove it, but I would assume that the SYS_CONTEXT is defined deterministic and just called once by the optimizer/query engine.
By
Patrick Wolf, at 01 December, 2006 09:18
Hi Patrick,
Do you have a working example of using sys_context with APEX.I would appreciate if you could point us to any post or share your expertise.
Thanks
Ashok
By
Ashok, at 21 February, 2008 02:32
Post a Comment
<< Home