oracle内联函数多次调用

时间:2014-07-07 22:12:48

标签: 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;

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


- 更多争论的相同例子 - 这里有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;

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

1 个答案:

答案 0 :(得分:2)

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

否则Oracle绝对不能保证SQL-Select中的函数执行的频率,它可能是每行一次,每行十次或整个查询只有一次(使用缓存) - 所以通常它被调用,这符合规范。

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

INSERT VALUES ( myFunc(x).attribute1, myFunc(x).attribute2 );

重要部分:在SQL语句中使用FUNCTION时,永远不要假设FUNCTION被调用的频率!在任何时候,优化器都可以再次调用该函数,可能用于采样或缓存......

首选解决方案:流水线函数 - 可以像表一样调用流水线函数,只调用一次。您可以传入函数用于输入处理的游标,并执行整个数据转换和日志记录以及函数中的所有内容。