当通过内联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;
增加对类型的参数调用的示例增加了函数执行的时间
--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;
非常感谢任何帮助/反馈。
答案 0 :(得分:2)
首先:这是一个错误,您甚至可以在SELECT语句中调用的函数内执行DML。这应该引发异常。
否则Oracle绝对不能保证SQL-Select中的函数执行的频率,它可能是每行一次,每行十次或整个查询只有一次(使用缓存) - 所以通常它被调用,这符合规范。
在这种特殊情况下,它会为返回类型的每个属性调用函数,因为oracle不会将对象类型作为一个内存结构插入,而是使用像多个列的表一样的函数,并像单独读取每个列一样这样:
INSERT VALUES ( myFunc(x).attribute1, myFunc(x).attribute2 );
重要部分:在SQL语句中使用FUNCTION时,永远不要假设FUNCTION被调用的频率!在任何时候,优化器都可以再次调用该函数,可能用于采样或缓存......
首选解决方案:流水线函数 - 可以像表一样调用流水线函数,只调用一次。您可以传入函数用于输入处理的游标,并执行整个数据转换和日志记录以及函数中的所有内容。