管道函数oracle中的动态sql
Posted
技术标签:
【中文标题】管道函数oracle中的动态sql【英文标题】:dynamic sql in a pipeline function oracle 【发布时间】:2021-06-20 19:49:39 【问题描述】:create or replace function fn_master(col_name VARCHAR2) return master_file pipelined
Is
TYPE MYMASTER_FILE IS RECORD (
id NUMBER(5),
name VARCHAR2(101),
school varchar2(2000)
);
TYPE MASTER_FILE_ARRAY IS TABLE OF MYMASTER_FILE
INDEX BY PLS_INTEGER;
l_info MASTER_FILE_ARRAY;
begin
execute immediate 'select id, name, school from x_table where ' || col_name || '= ''xxxxxx'' '
BULK COLLECT INTO l_info;
FOR i IN 1 .. l_info.COUNT
LOOP
PIPE row(master_file(
l_info(i).id,
l_info(i).name ,
l_info(i).school
));
END LOOP;
end fn_master;
这是我创建的函数,用于接受该表中的列并打印返回的行。为了在堆栈溢出时将其呈现在此处,我将其简化了。
当我尝试编译时出现错误
PLS-00630: pipelined functions must have a supported collection return type
但是当我添加一个返回子句时它告诉我
PLS-00633: RETURN statement in a pipelined function cannot contain an expression
PLS-00382: expression is of wrong type
PLS-00630: pipelined functions must have a supported collection return type
你们能帮我解决这个问题吗
【问题讨论】:
是我接近函数的方式,正确的方式吗? 【参考方案1】:您的代码存在一些问题,与返回类型有关,而不是与动态 SQL 的使用有关。
首先,流水线函数的返回类型应该是表类型,而不是行类型。您已将其设置为 master_file
。 (顺便说一句,你实际上并没有在任何地方定义过这种类型,但我们不要纠缠于此。也许你的意思是mymaster_file
?)
但是,如果您随后将其设置为 master_file_array
,则会收到错误 PLS-00498: illegal use of a type before its declaration
。在某种程度上,这是有道理的:Oracle 不编译该函数不知道该函数返回什么类型,但它需要知道返回类型才能编译该函数。
因此,让我们定义一个对象类型和一个嵌套表类型,这样我们就可以将其用作返回类型:
CREATE OR REPLACE TYPE MASTER_FILE AS OBJECT (
id NUMBER(5),
name VARCHAR2(101),
school varchar2(2000)
);
/
CREATE TYPE MASTER_FILE_ARRAY AS TABLE OF MASTER_FILE;
/
然后我尝试将这些类型与您的EXECUTE IMMEDIATE
语句的BULK COLLECT INTO
子句一起使用,认为您在函数中声明的类型不再需要了。然而,当我这样做时,我得到了一个非常有用的错误ORA-00932: inconsistent datatypes: expected - got -
。看来您只能将 PL/SQL 关联数组(即使用 INDEX BY
子句声明的集合)与 BULK COLLECT INTO
一起使用,所以让我们再次尝试使用您在函数中定义的集合类型:
create or replace function fn_master(col_name VARCHAR2) return master_file_array pipelined
is
TYPE MYMASTER_FILE IS RECORD (
id NUMBER(5),
name VARCHAR2(101),
school varchar2(2000)
);
TYPE MYMASTER_FILE_ARRAY IS TABLE OF MYMASTER_FILE
INDEX BY PLS_INTEGER;
l_info MYMASTER_FILE_ARRAY;
begin
execute immediate 'select id, name, school from x_table where ' || col_name || '= ''xxxxxx'' ' BULK COLLECT INTO l_info;
FOR i IN 1 .. l_info.COUNT
LOOP
PIPE row(master_file(
l_info(i).id,
l_info(i).name ,
l_info(i).school
));
END LOOP;
end fn_master;
/
请注意,我使用前缀MY
作为函数内声明的类型的名称。函数中的 PL/SQL 关联数组类型的名称现在是 MYMASTER_FILE_ARRAY
,并且 l_info
被声明为这种类型。函数外声明的类型名称没有这个前缀,所以函数返回MASTER_FILE_ARRAY
,调用PIPE ROW(...)
使用MASTER_FILE
。
我创建了一个表x_table
并在其中插入了一些数据,在进行上述修改后,我能够调用您的函数并让它从该表中返回一些数据。
【讨论】:
【参考方案2】:流水线函数的要点是能够将一组结果流回调用者,而无需将整个集合实例化为内存中的集合。这意味着您可以在第一行可用时立即开始返回行,而无需等待最后一行或为整个集合分配内存。发布的函数在管道任何东西之前将所有行获取到一个数组中,因此首先将其管道化似乎没有任何意义。
真正的流水线实现可能如下所示:
create or replace function fn_master
( col_name varchar2 )
return master_file_array
pipelined
as
c_results sys_refcursor;
r master_file := master_file(null, null, null);
begin
open c_results for 'select id, name, school from demo where ' || col_name || '= ''ABC'' ';
loop
fetch c_results into r.id, r.name, r.school;
exit when c_results%notfound;
pipe row(r);
end loop;
end fn_master;
顺便说一句,我建议在代码布局上多加注意,因为它使代码更易于理解、调试和维护。
【讨论】:
以上是关于管道函数oracle中的动态sql的主要内容,如果未能解决你的问题,请参考以下文章
Oracle:使用 SQL 或 PL/SQL 查找动态 SQL 中的错误位置