合并两个 sys_refcursor 的输出

Merge the output of two sys_refcursor

我在 oracle 11g 中有两个函数,return 一个 sys_refcursos。 本次第一个

create or replace FUNCTION num_gettoni
    (cf_parlamentare IN parlamentari.cf %TYPE DEFAULT 'MRTMRZ'
      --, num_legislatura in legislature.id%TYPE
    ) RETURN SYS_REFCURSOR
  AS
    my_cursor SYS_REFCURSOR;
    pippo legislature.id%type;
  BEGIN

    OPEN my_cursor FOR

    select
      leg, 
      ct as gettoni
    from( 
       SELECT
         l.id AS leg,
         COUNT(*) - lead(COUNT(*), 1, 0) over (order by l.datainizio) AS ct
       FROM
         legislature l,
         partecipazioni i,
         parlamentari p
       WHERE 
         i.sedute_data >= l.datainizio 
         AND p.cf = i.parlamentare 
         AND p.cf = cf_parlamentare

       group by l.datainizio, l.id
    )

    where ct > 0
    order by ct desc;

    /*open my_cursor;
    loop
    pippo := my_cursor.leg;
    END LOOP;

    end loop;*/

    RETURN my_cursor;
  END num_gettoni;

第一个函数的输出示例是

select num_gettoni('MRTMRZ') from dual;

NUM_GETTONI('MRTMRZ') 
--------------------- 
LEG                    GETTONI                 
---------------------- ----------------------  
17                     3                       
18                     2

第二个函数类似,第二个函数的输出为

select num_interrogazioni('MRTMRZ') from dual;

NUM_INTERROGAZIONI('MRTMRZ') 
-------------------------------------- 
LEG                    INTERROGAZIONI          
---------------------- ----------------------  
18                     1                     

是否可以通过过程调用这些函数并得到类似如下的结果?

NUM_INTERROGAZIONI('MRTMRZ') 
    -------------------------------------- 
    LEG                    GETTONI                 INTERROGAZIONI 
    ---------------------- ---------------------- ----------------------  
    17                     3                       
    18                     2                       1

没有简单的内置机制来合并引用游标;它们本质上是指向结果集的指针,它们不能被视为 tables,所以它们不能被连接,这基本上就是您要在此处实现的目标。

如果您只想显示组合结果,您可以使用 PL/SQL 集合来存储第一个引用游标的结果,然后 update/add 将第二个引用游标的结果存储到它,基于 leg 是常见的 'key' 值:

declare
  -- for the collection
  type t_rec is record (leg number, gettoni number, interrogazioni number);
  type t_tab is table of t_rec index by pls_integer;
  l_tab t_tab;
  -- for the cursors returned by the functions
  l_cursor sys_refcursor;
  -- for the individual columns from the cursors
  l_leg number;
  l_gettoni number;
  l_interrogazioni number;
begin
  l_cursor := num_gettoni('MRTMRZ');
  loop
    fetch l_cursor into l_leg, l_gettoni;
    exit when l_cursor%notfound;
    l_tab(l_leg).leg := l_leg;
    l_tab(l_leg).gettoni := l_gettoni;
  end loop;
  close l_cursor;

  l_cursor := num_interrogazioni('MRTMRZ');
  loop
    fetch l_cursor into l_leg, l_interrogazioni;
    exit when l_cursor%notfound;
    l_tab(l_leg).leg := l_leg;
    l_tab(l_leg).interrogazioni := l_interrogazioni;
  end loop;
  close l_cursor;

  for i in l_tab.first..l_tab.last loop
    dbms_output.put_line(l_tab(i).leg ||','|| l_tab(i).gettoni ||','|| l_tab(i).interrogazioni);
  end loop;
end;
/

使用虚拟函数 return 您显示的结果,得到:

17,3,
18,2,1


PL/SQL procedure successfully completed.

这两个游标循环本质上是一样的。调用相关函数并遍历结果,在索引集合元素中为游标具有的列设置值;在这两种情况下,索引都是 leg 值。

第一个循环为索引 17 和 18 处的记录元素填充 leggettoni 值。第二个循环只看到 18 的结果并为此设置 interrogazioni元素。如果它也有一个不同的 leg,比如 19,那么它也会用 leginterrogazioni 值填充具有该索引的元素。 (所以,本质上,它大致相当于一个完整的外部连接...)

但是依赖 dbms_output 输出并不理想,因为您无法控制客户端是否正在使用它,而且它更难格式化和使用。将结果返回为可在查询中使用的 table 集合或可能作为新的引用游标可能会更有用。

您可以使用一个包来定义集合类型,其中一个函数使用上述机制将结果作为 table 集合通过管道输出,第二个函数从该管道生成一个引用游标table:

create or replace package p42 as
  type t_rec is record (leg number, gettoni number, interrogazioni number);
  type t_tab is table of t_rec;

  -- function for pipelined table collection
  function num_combo_tab (p_param varchar2) return t_tab pipelined;
  -- function for ref cursor
  function num_combo_cur (p_param varchar2) return sys_refcursor;
end p42;
/
create or replace package body p42 as
  -- function for pipelined table collection
  function num_combo_tab (p_param varchar2) return t_tab pipelined is
    type t_tmp_tab is table of t_rec index by pls_integer;
    l_tab t_tmp_tab;
    l_leg number;
    l_gettoni number;
    l_interrogazioni number;
    l_cursor sys_refcursor;
  begin
    l_cursor := num_gettoni(p_param);
    loop
      fetch l_cursor into l_leg, l_gettoni;
      exit when l_cursor%notfound;
      l_tab(l_leg).leg := l_leg;
      l_tab(l_leg).gettoni := l_gettoni;
    end loop;
    close l_cursor;

    l_cursor := num_interrogazioni(p_param);
    loop
      fetch l_cursor into l_leg, l_interrogazioni;
      exit when l_cursor%notfound;
      l_tab(l_leg).leg := l_leg;
      l_tab(l_leg).interrogazioni := l_interrogazioni;
    end loop;
    close l_cursor;

    for i in l_tab.first..l_tab.last loop
      pipe row (l_tab(i));
    end loop;
  end num_combo_tab;

  -- function for ref cursor
  function num_combo_cur (p_param varchar2) return sys_refcursor is
    l_cursor sys_refcursor;
  begin
    open l_cursor for
      select * from table(num_combo_tab(p_param));
    return l_cursor;
  end num_combo_cur;
end p42;
/

这里的num_combo_tab基本上是上面的匿名块,但是它管道记录类型而不是使用dbms_output。然后 num_combo_cur 只是为该结果打开一个引用游标。

那么你可以这样做:

select p42.num_combo_cur('MRTMRZ') from dual;

P42.NUM_COMBO_CUR('M
--------------------
CURSOR STATEMENT : 1

CURSOR STATEMENT : 1

       LEG    GETTONI INTERROGAZIONI
---------- ---------- --------------
        17          3               
        18          2              1

或者直接使用table版本:

select * from table(p42.num_combo_tab('MRTMRZ'));

       LEG    GETTONI INTERROGAZIONI
---------- ---------- --------------
        17          3               
        18          2              1

如果您愿意,您也可以使用架构级对象和 table 类型以及架构级函数来执行此操作:

create type t_obj as object (leg number, gettoni number, interrogazioni number)
/
create type t_tab is table of t_obj
/

create or replace function num_combo_tab (p_param varchar2)
return t_tab pipelined as
  type t_tmp_tab is table of t_obj index by pls_integer;
  l_tab t_tmp_tab;
  l_leg number;
  l_gettoni number;
  l_interrogazioni number;
  l_cursor sys_refcursor;
begin
  l_cursor := num_gettoni(p_param);
  loop
    fetch l_cursor into l_leg, l_gettoni;
    exit when l_cursor%notfound;
    l_tab(l_leg) := new t_obj(l_leg, l_gettoni, null);
  end loop;
  close l_cursor;

  l_cursor := num_interrogazioni(p_param);
  loop
    fetch l_cursor into l_leg, l_interrogazioni;
    exit when l_cursor%notfound;
    if l_tab.exists(l_leg) then
      l_tab(l_leg).interrogazioni := l_interrogazioni;
    else
      l_tab(l_leg) := new t_obj(l_leg, null, l_interrogazioni);
    end if;
  end loop;
  close l_cursor;

  for i in l_tab.first..l_tab.last loop
    pipe row (l_tab(i));
  end loop;
end num_combo_tab;
/

那么你可以这样称呼它:

select * from table(num_combo_tab('MRTMRZ'));

       LEG    GETTONI INTERROGAZIONI
---------- ---------- --------------
        17          3               
        18          2              1

但无论如何,将此功能(可能还有您的原始功能)放在一个包中可能更明智。


在以上所有内容中,很明显,请尽可能使用您自己的数据类型和 %type,我没有您的 table,因此我使用了例如p_param varchar 而不是现有函数声明其参数的方式。