A function in a subquery is call too many times.

Hi all

I am struggling to understand why a function in a subquery is called too many times.
Let me explain with an example:
create or replace function all_emp (v_deptno in number) 
return varchar2
as

v_all_emp varchar2(2000);

begin

    dbms_output.put_line ('function called');

    for i in (select * from emp where deptno = v_deptno) loop

        v_all_emp := v_all_emp || i.ename || '; ';
    
    end loop;

return v_all_emp;
    
end;
/

-- running just the subquery, calls the function all_emp only 4 times (once for each row in table dept)
select 
    d.deptno,
    d.dname,
    all_emp(d.deptno) f_all_emp
    from dept d;

-- running the whole query, using regexp to split the value of f_all_emp into separate fields, causes that function all_emp is called 28 times, thus 6 times for each row!!
select tmp.*,
regexp_substr(f_all_emp,'[^;]*',1,1) emp1,
regexp_substr(f_all_emp,'[^;]*',1,3) emp2,
regexp_substr(f_all_emp,'[^;]*',1,5) emp3,
regexp_substr(f_all_emp,'[^;]*',1,7) emp4,
regexp_substr(f_all_emp,'[^;]*',1,9) emp5,
regexp_substr(f_all_emp,'[^;]*',1,11) emp6
from
    (select 
    d.deptno,
    d.dname,
    all_emp(d.deptno) f_all_emp
    from dept d) tmp
;
I do not understand why Oracle calls my 28 times function in this example, 4 times should be sufficient.

Is there a way to force that the subquery is materialized first?

A reminder of the facts:
Above function / request is of course a simple example.
In fact, I have a rather complex function, integrating in a subquery.
The subquery is already slow (2 min) to run, but when I want to share the result of the function in several areas (about 20) it's over an hour due to above described behavior.

Optimizer merges online view and the query results in:

select  d.deptno,
        d.dname,
        all_emp(d.deptno) f_all_emp
        regexp_substr(all_emp(d.deptno),'[^;]*',1,1) emp1,
        regexp_substr(all_emp(d.deptno),'[^;]*',1,3) emp2,
        regexp_substr(all_emp(d.deptno),'[^;]*',1,5) emp3,
        regexp_substr(all_emp(d.deptno),'[^;]*',1,7) emp4,
        regexp_substr(all_emp(d.deptno),'[^;]*',1,9) emp5,
        regexp_substr(all_emp(d.deptno),'[^;]*',1,11) emp6
  from  dept d
/

That's why function is called 28 times. Seen go explain plan:

SQL> explain plan for
  2  select tmp.*,
  3          regexp_substr(f_all_emp,'[^;]*',1,1) emp1,
  4          regexp_substr(f_all_emp,'[^;]*',1,3) emp2,
  5          regexp_substr(f_all_emp,'[^;]*',1,5) emp3,
  6          regexp_substr(f_all_emp,'[^;]*',1,7) emp4,
  7          regexp_substr(f_all_emp,'[^;]*',1,9) emp5,
  8          regexp_substr(f_all_emp,'[^;]*',1,11) emp6
  9    from  (
 10           select  d.deptno,
 11                   d.dname,
 12                   all_emp(d.deptno) f_all_emp
 13             from  dept d
 14          ) tmp
 15  /

Explained.

SQL> @?\rdbms\admin\utlxpls

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------
Plan hash value: 3383998547

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |     4 |    52 |     3   (0)| 00:00:01 |
|   1 |  TABLE ACCESS FULL| DEPT |     4 |    52 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------

8 rows selected.

SQL>  

If we use the NO_MERGE indicator:

SQL> select  /*+ NO_MERGE(tmp) */
  2          tmp.*,
  3          regexp_substr(f_all_emp,'[^;]*',1,1) emp1,
  4          regexp_substr(f_all_emp,'[^;]*',1,3) emp2,
  5          regexp_substr(f_all_emp,'[^;]*',1,5) emp3,
  6          regexp_substr(f_all_emp,'[^;]*',1,7) emp4,
  7          regexp_substr(f_all_emp,'[^;]*',1,9) emp5,
  8          regexp_substr(f_all_emp,'[^;]*',1,11) emp6
  9    from  (
 10           select  d.deptno,
 11                   d.dname,
 12                   all_emp(d.deptno) f_all_emp
 13             from  dept d
 14          ) tmp
 15  /

    DEPTNO DNAME          F_ALL_EMP  EMP1       EMP2       EMP3       EMP4       EMP5       EMP6
---------- -------------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
        10 ACCOUNTING     CLARK; KIN CLARK       KING       MILLER
                          G; MILLER;

        20 RESEARCH       SMITH; JON SMITH       JONES      SCOTT      ADAMS      FORD
                          ES; SCOTT;
                           ADAMS; FO
                          RD;

        30 SALES          ALLEN; WAR ALLEN       WARD       MARTIN     BLAKE      TURNER     JAMES
                          D; MARTIN;

    DEPTNO DNAME          F_ALL_EMP  EMP1       EMP2       EMP3       EMP4       EMP5       EMP6
---------- -------------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
                           BLAKE; TU
                          RNER; JAME
                          S;

        40 OPERATIONS

function called
function called
function called
function called
function called
function called
SQL> explain plan for
  2  select  /*+ NO_MERGE(tmp) */
  3          tmp.*,
  4          regexp_substr(f_all_emp,'[^;]*',1,1) emp1,
  5          regexp_substr(f_all_emp,'[^;]*',1,3) emp2,
  6          regexp_substr(f_all_emp,'[^;]*',1,5) emp3,
  7          regexp_substr(f_all_emp,'[^;]*',1,7) emp4,
  8          regexp_substr(f_all_emp,'[^;]*',1,9) emp5,
  9          regexp_substr(f_all_emp,'[^;]*',1,11) emp6
 10    from  (
 11           select  d.deptno,
 12                   d.dname,
 13                   all_emp(d.deptno) f_all_emp
 14             from  dept d
 15          ) tmp
 16  /

Explained.

SQL> @?\rdbms\admin\utlxpls

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------
Plan hash value: 2317111044

---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |     4 |  8096 |     3   (0)| 00:00:01 |
|   1 |  VIEW              |      |     4 |  8096 |     3   (0)| 00:00:01 |
|   2 |   TABLE ACCESS FULL| DEPT |     4 |    52 |     3   (0)| 00:00:01 |
---------------------------------------------------------------------------

9 rows selected.

SQL> 

Not sure why the function is executed once 6 and not 4. What we really want, is to materialize reviews online:

SQL> with tmp as (
  2               select  /*+ materialize */
  3                       d.deptno,
  4                       d.dname,
  5                       all_emp(d.deptno) f_all_emp
  6                 from  dept d
  7              )
  8  select  tmp.*,
  9          regexp_substr(f_all_emp,'[^;]*',1,1) emp1,
 10          regexp_substr(f_all_emp,'[^;]*',1,3) emp2,
 11          regexp_substr(f_all_emp,'[^;]*',1,5) emp3,
 12          regexp_substr(f_all_emp,'[^;]*',1,7) emp4,
 13          regexp_substr(f_all_emp,'[^;]*',1,9) emp5,
 14          regexp_substr(f_all_emp,'[^;]*',1,11) emp6
 15    from  tmp
 16  /

    DEPTNO DNAME          F_ALL_EMP  EMP1       EMP2       EMP3       EMP4       EMP5       EMP6
---------- -------------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
        10 ACCOUNTING     CLARK; KIN CLARK       KING       MILLER
                          G; MILLER;

        20 RESEARCH       SMITH; JON SMITH       JONES      SCOTT      ADAMS      FORD
                          ES; SCOTT;
                           ADAMS; FO
                          RD;

        30 SALES          ALLEN; WAR ALLEN       WARD       MARTIN     BLAKE      TURNER     JAMES
                          D; MARTIN;

    DEPTNO DNAME          F_ALL_EMP  EMP1       EMP2       EMP3       EMP4       EMP5       EMP6
---------- -------------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
                           BLAKE; TU
                          RNER; JAME
                          S;

        40 OPERATIONS

function called
function called
function called
function called
SQL> explain plan for
  2  with tmp as (
  3               select  /*+ materialize */
  4                       d.deptno,
  5                       d.dname,
  6                       all_emp(d.deptno) f_all_emp
  7                 from  dept d
  8              )
  9  select  tmp.*,
 10          regexp_substr(f_all_emp,'[^;]*',1,1) emp1,
 11          regexp_substr(f_all_emp,'[^;]*',1,3) emp2,
 12          regexp_substr(f_all_emp,'[^;]*',1,5) emp3,
 13          regexp_substr(f_all_emp,'[^;]*',1,7) emp4,
 14          regexp_substr(f_all_emp,'[^;]*',1,9) emp5,
 15          regexp_substr(f_all_emp,'[^;]*',1,11) emp6
 16    from  tmp
 17  /

Explained.

SQL> @?\rdbms\admin\utlxpls

PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------------------------------------------------
Plan hash value: 634594723

---------------------------------------------------------------------------------------------------------
| Id  | Operation                  | Name                       | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT           |                            |     4 |  8096 |     5   (0)| 00:00:01 |
|   1 |  TEMP TABLE TRANSFORMATION |                            |       |       |            |          |
|   2 |   LOAD AS SELECT           |                            |       |       |            |          |
|   3 |    TABLE ACCESS FULL       | DEPT                       |     4 |    52 |     3   (0)| 00:00:01 |
|   4 |   VIEW                     |                            |     4 |  8096 |     2   (0)| 00:00:01 |
|   5 |    TABLE ACCESS FULL       | SYS_TEMP_0FD9D6603_20255AE |     4 |    52 |     2   (0)| 00:00:01 |

PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------

12 rows selected.

SQL> 

However, suspicion of MATERIALIZATION is a hint of undocumented.

SY.

Tags: Database

Similar Questions

Maybe you are looking for