【问题标题】:How do I write a stored procedure which adds a column to a ref cursor from another stored procedure?如何编写一个存储过程,将列添加到另一个存储过程的引用游标?
【发布时间】:2016-05-30 20:27:04
【问题描述】:

我有一个存储过程,GetRegions,它将一些列提取到游标中,如下所示:

OPEN out_cur FOR
    SELECT id, name, ...
      FROM regions, ...
     WHERE ...

这个SP到处都在使用——在会返回几十个地区的地方,在会返回数万个的地方。

我还有一个函数,GetRegionPath,它获取一个区域的“路径”。这是一个中等成本的功能 - 在数十个区域上运行它没有问题,但在数万个区域上运行它是不可接受的。

我需要编写一个存储过程,GetRegionsWithPaths,它使用与GetRegions 完全相同的逻辑现在和永远获取区域,但在结果中包含区域的路径设置。

目前,GetRegionsWithPathsGetRegions 的精确副本,并添加了路径:

OPEN out_cur FOR
    SELECT id, name, ..., GetRegionPath(id) path
      FROM regions, ...
     WHERE ...

但这是不可接受的 - 如果有人编辑 GetRegions,那么这两个 SP 将不同步。我想要的是从GetRegions 中取出光标,然后添加它的路径。比如:

GetRegions(..., v_cur);

OPEN out_cur FOR
    SELECT id, name, ..., GetRegionPath(id) path
        FROM ( SELECT * FROM v_cur );

这可能吗?如果有,语法是什么?

【问题讨论】:

  • 我会向 GetRegions SP 添加一个名为“includedPath”的额外参数(0-否,1-是)。 SP 将始终返回“路径”列,但如果参数为“0”,它将为 NULL。然后在光标上添加一个额外的列来调用“GetPath”,如下所示“CASE WHEN includePath = 1 THEN GetRegionPath(id) ELSE NULL END CASE”。
  • @MarcoPolo 这是一个非常好的建议,谢谢。
  • 您甚至可以根据参数动态打开两个不同的游标,一个带列,另一个不带列。
  • @MarmiteBomber 我不确定这对我有什么帮助 - 我需要相同的逻辑,所以我仍然需要从第一个光标读取以写入第二个光标。
  • 添加了@Maco提出的参数实现

标签: oracle stored-procedures plsql oracle11g


【解决方案1】:

使用 cmets 中建议的参数的替代实现。

假设原来的函数是这样的

create or replace function get_cur return SYS_REFCURSOR is
cv_out     sys_refcursor;
begin
 OPEN cv_out  
      FOR 
      select   Id,   name from tab;
 return cv_out;
end;
/

您添加一个参数return_path,该参数为 0 - 返回原始光标或 1 - 将返回带有路径列的原始光标。

实现可能是这样的

create or replace function get_cur_with_param(return_path NUMBER) return SYS_REFCURSOR is
cv_out     sys_refcursor;
v_stmt_str      VARCHAR2(4000);
begin
 v_stmt_str := 'select   Id,   name '||case when return_path = 1 then ', GetRegionPath(id)' end ||' from tab';
 OPEN cv_out  FOR v_stmt_str; 
 return cv_out;
end;
/

get_cur_with_param(0) 返回 2 列游标,get_cur_with_param(1) 返回添加了 path 列的游标。

请注意,SQL 是动态构建的,但只会以两个变体结尾,因此解析不会有问题。如果查询中有其他参数,请使用添加 USING 子句的绑定变量。

【讨论】:

    【解决方案2】:

    一种可能的解决方案(最终可以简化)是使用表函数来处理光标并添加函数值。

    假设返回 sys_refcursor 的函数称为 get_cur,并且游标由列 ID, NAME 组成(这很重要,因为表函数需要类型定义)。

    您为行(包括附加的路径列)和结果表声明 TYPE。

    create type t_row is object
     ( id             number(10),
       name varchar2(10),
       path varchar2(10)
    );
    /
    
    create type t_rows is table of t_row;
    /
    

    并定义获取光标并添加函数调用的表函数。

    create or replace function get_cur2  return 
    t_rows
    PIPELINED
    as
       cv_out     sys_refcursor;
       id   number;
       name   varchar2(100);    
    begin
          cv_out := get_cur; 
          loop 
            FETCH cv_out INTO id, name;
            exit when cv_out%NOTFOUND;
            pipe row(t_row(id,name, GetRegionPath(id)));
          end loop;
          close    cv_out;
          return;
    end;
    /
    

    现在你可以从表格函数中选择数据了

    select * from  table(get_cur2); 
    
            ID NAME       PATH     
    ---------- ---------- ----------
             1 one        path 1     
             2 two        path 2 
    

    当然,您可以使用此查询在第三个函数中打开一个游标,该函数将返回带有附加路径列的 SYS_REFCURSOR。

    【讨论】:

    • 谢谢 - 有没有一种方法不需要我知道第一个游标的行类型?
    • @Simon 表函数需要强类型定义,但请检查第二个答案中的替代方法
    猜你喜欢
    • 2010-11-14
    • 1970-01-01
    • 2014-09-17
    • 2011-05-08
    • 1970-01-01
    • 2013-03-08
    • 1970-01-01
    • 2019-10-13
    相关资源
    最近更新 更多