I've created a package, containing a function that returns an object.
When retrieving the object details through sql, the function is called multiple times - once for every detail retrieved.
I believe it should be possible for it to just be called once instead.
Following is an example that demonstrates the issue:
CREATE OR REPLACE TYPE t_test AS OBJECT (
v1 VARCHAR2(10),
v2 VARCHAR2(10),
v3 VARCHAR2(10),
times_called NUMBER
);
/
CREATE OR REPLACE PACKAGE test_pkg AS
times_called NUMBER :=0;
FUNCTION test(something IN VARCHAR2) RETURN t_test;
PROCEDURE reset;
END test_pkg;
/
CREATE OR REPLACE PACKAGE BODY test_pkg IS
PROCEDURE reset IS
BEGIN
times_called := 0;
END;
FUNCTION test(something IN VARCHAR2) RETURN t_test IS
BEGIN
times_called := times_called 1;
RETURN t_test('first', 'second', 'third', times_called);
END;
END test_pkg;
/
Here we can see that the function is invoked four times:
SQL> SELECT t.r.v1, t.r.v2, t.r.v3, t.r.times_called FROM (
2 SELECT test_pkg.test('x') r FROM DUAL
3 ) t;
R.V1 R.V2 R.V3 R.TIMES_CALLED
---------- ---------- ---------- --------------
first second third 4
SQL>
If we reset the counter, and only select two attributes, we can see it's called twice:
SQL> exec test_pkg.reset();
PL/SQL procedure successfully completed.
SQL> SELECT t.r.v1, t.r.times_called FROM (
2 SELECT test_pkg.test('x') r FROM DUAL
3 ) t;
R.V1 R.TIMES_CALLED
---------- --------------
first 2
SQL>
The actual stored procedure is more expensive, so I'd like to avoid re-calling it for every attribute listed.
The solution has to work on Oracle 10gr2
CodePudding user response:
Oracle is not materializing the sub-query and is pushing the function calls to the outer query. You need to force the SQL engine to materialize the inner query either by:
Using a seemingly unnecessary ROWNUM > 0
filter:
SELECT t.r.v1, t.r.v2, t.r.v3, t.r.times_called
FROM (
SELECT test_pkg.test('x') r
FROM DUAL
WHERE ROWNUM > 0
) t;
or, you should be able to use the (undocumented) /* materialize */
hint but, for an unknown reason, it doesn't seem to want to materialize this particular query (although it does work for similar problems).
db<>fiddle here