管道函数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的主要内容,如果未能解决你的问题,请参考以下文章

用于动态 sql 透视的 Oracle 函数

Oracle pl sql 动态使用子句

Oracle:使用 SQL 或 PL/SQL 查找动态 SQL 中的错误位置

从 Oracle 中的动态 SQL 获取结果集中的结果

包含使用 with 子句的 sql 的 oracle 管道函数

oracle sql中的动态列