我创建了一个包,其中包含一个返回对象的函数。
当通过 sql 检索对象详细信息时,该函数会被多次调用 - 每检索一个详细信息一次。
我相信应该可以只调用一次。
以下是演示该问题的示例:
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;
/
Run Code Online (Sandbox Code Playgroud)
这里我们可以看到该函数被调用了四次:
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>
Run Code Online (Sandbox Code Playgroud)
如果我们重置计数器,并且只选择两个属性,我们可以看到它被调用了两次:
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>
Run Code Online (Sandbox Code Playgroud)
实际的存储过程更昂贵,因此我想避免为列出的每个属性重新调用它。
该解决方案必须适用于 Oracle 10gr2
Oracle 没有具体化子查询,而是将函数调用推送到外部查询。您需要通过以下方式强制 SQL 引擎具体化内部查询:
使用看似不必要的ROWNUM > 0过滤器:
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;
Run Code Online (Sandbox Code Playgroud)
或者,您应该能够使用(未记录的)/*+ materialize */提示,但是由于未知的原因,它似乎不想实现这个特定的查询(尽管它确实适用于类似的问题)。
您还可以(正如William Robertson在评论中指出的那样)使用“导致 Oracle 不合并可合并视图”的/*+ NO_MERGE */提示:
SELECT t.r.v1, t.r.v2, t.r.v3, t.r.times_called
FROM (
SELECT /*+ no_merge */
test_pkg.test('x') r
FROM DUAL
) t;
Run Code Online (Sandbox Code Playgroud)
db<>在这里摆弄