Skip to Main Content
  • Questions
  • Function returning object type is unexpectedly called multiple times, when individually properties are accessed in the same query

Breadcrumb

Question and Answer

Chris Saxon

Thanks for the question, Jos.

Asked: March 25, 2022 - 9:52 am UTC

Last updated: March 25, 2022 - 4:32 pm UTC

Version: 19c, both Standard and Enterprise Edition

Viewed 10K+ times! This question is

You Asked

Hi TOM,

We bumped into an issue, which we boiled down to a function being called multiple times, when we expected it to be called only once.

The demo script shows that the function is called once when invoking the function in a `select from dual` query, but twice if that query is wrapped in a query that extracts the individual properties from the result object. If more properties are added and extracted, the function is called more times as well.

We expected the function to be called once in the inner query, after which the outer query would just read properties from the single returned object instance.

We noticed this on 19c Enterprise, but experienced the same in 19c Standard. The LiveSQL (running on 19c as well) also shows the same effect.

Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Version 19.13.0.0.0
Oracle Database 19c Standard Edition 2 Release 19.0.0.0.0 - Production
Version 19.13.0.0.0
The LiveSQL running on 19c Enterprise Edition - 19.8.0.0.0 shows the same results

We did find a workaround, by making the function return a table type. It's good enough to continue with, but it does semantically hide the fact that the function will return exactly one object, so it's not ideal.

We're curious why this happens, and of course also like to know the 'proper' way to do this without using this table type workaround.

Regards,
Jos

with LiveSQL Test Case:

and Chris said...

Each expression in the select clause is a new call to the function. You can see this with a regular function call:

create or replace function f
  return int as
begin
  dbms_output.put_line('Executing function'); 
  return 1;
end f;
/

select n, n, n
from   (
  select f n from dual
);

         N          N          N
---------- ---------- ----------
         1          1          1

Executing function
Executing function
Executing function


The trick is to put the function inside a scalar subquery - i.e. a select in the select.

Then subquery caching kicks in and the function only executes once:

select n, n, n
from (
select ( select f from dual ) n from dual
);

N N N
---------- ---------- ----------
1 1 1

Executing function

Rating

  (1 rating)

Is this answer out of date? If it is, please let us know via a Comment

Comments

Scalar!

Jos, March 25, 2022 - 1:28 pm UTC

That's it, thanks! I've been wrapping function calls in dual subqueries so many times, without fully realizing it was the scalar subquery cache that did the trick.

In fact, I may have tried in the past to optimize a query like this:

select
  FNC() as X
from
  SOMETABLE;


To this, being disappointed in the lack of improvement.

select
  X
from
  SOMETABLE
  cross join (select FNC() as X from dual);


That would still feel logical, but the difference is clear. I'm going to try and remove some false assumptions from my brain.
Thanks again!
Chris Saxon
March 25, 2022 - 4:32 pm UTC

Happy to help

More to Explore

PL/SQL demos

Check out more PL/SQL tutorials on our LiveSQL tool.

PL/SQL docs

PL/SQL reference manual from the Oracle documentation library