oracle内联函数多次调用

wil*_*ejj 5 sql oracle

当通过内联select语句调用函数时,当函数返回自定义类型时,Oracle似乎执行的函数等于参数+1的数量.当选择包括在CTAS或插入/选择中时,似乎会发生这种情况.

谁看过这个吗?这是Oracle的错误吗?我希望在表中每行调用一次该函数.

--Inline function gets called for the number of arguments +1

--drop table t
create table t(
  id number,
  l_blob blob
);


insert into t values(1, utl_raw.cast_to_raw('SampleString'));
COMMIT;

create table tmp_ts (c1 timestamp); 

create or replace type test_type as object(
              c1         varchar2(32)
              ,c2  varchar2(32)
        );
/

create or replace FUNCTION test_function (p_blob blob, p_date date)
RETURN test_type
IS
BEGIN

--This could also be a DBMS_OUTPUT.PUT_LINE statement
insert into tmp_ts VALUES (systimestamp);

return test_type(null,null);

END test_function;
/

--0
select count(*) from tmp_ts;

--Call function on 1 row table - function should just insert 1 row into tmp_ts
create table tst_table as
select test_function(l_blob, '25-JAN-09') as c1
from t;

--it actually inserts 3
select count(*) from tmp_ts;
Run Code Online (Sandbox Code Playgroud)

增加类型的参数调用的示例增加了函数执行的时间


- 有更多争论的例子 - 这里有6个争论

--Inline function gets called for the number of arguments +1

--drop table t
create table t2(
  id number,
  l_blob blob
);


insert into t2 values(1, utl_raw.cast_to_raw('SampleString'));
COMMIT;

create table tmp_ts2 (c1 timestamp); 

create or replace type test_type2 as object(
              c1         varchar2(32)
              ,c2  varchar2(32)
              ,c3  varchar2(32)
              ,c4  varchar2(32)
              ,c5  varchar2(32)
              ,c6  varchar2(32)
        );
/

create or replace FUNCTION test_function2 (p_blob blob, p_date date)
RETURN test_type2
IS
BEGIN

insert into tmp_ts2 VALUES (systimestamp);

return test_type2(null,null,null,null,null,null);

END test_function2;
/

--0
select count(*) from tmp_ts2;

--Call function on 1 row table - function should just insert 1 row into tmp_ts
create table tst_table2 as
select test_function2(l_blob, '25-JAN-09') as c1
from t;

--it actually inserts 7
select count(*) from tmp_ts2;
Run Code Online (Sandbox Code Playgroud)

非常感谢任何帮助/反馈.

Fal*_*lco 2

首先:这是一个错误,您甚至可以在 SELECT 语句中调用的函数内执行 DML。这应该引发异常。

否则,Oracle 绝对不保证 SQL-Select 中函数的执行频率,可能每行执行一次、每行十次或整个查询仅执行一次(带缓存) - 因此无论调用频率如何,这都符合规格。

在这种特殊情况下,它将为返回类型的每个属性调用该函数,因为 Oracle 不会将对象类型作为一个内存结构插入,而是像具有多列的表一样使用该函数,并单独读取每一列,如下所示:

INSERT VALUES ( myFunc(x).attribute1, myFunc(x).attribute2 );
Run Code Online (Sandbox Code Playgroud)

The important part: Never make any assumptions about how often a FUNCTION is called when you use it in an SQL Statement!!! At any time the function could be called again by the optimizer, maybe for sampling or caching...

Preferred solution: Pipelined Functions - a pipelined Function can be called like a Table and will only be called once. You can pass in a cursor which the function uses for input processing and do the whole data-transformation and logging and everything in the function.