BULK COLLECT INTO inside OPEN cursor FOR SELECT... 不填充集合

Posted

技术标签:

【中文标题】BULK COLLECT INTO inside OPEN cursor FOR SELECT... 不填充集合【英文标题】:BULK COLLECT INTO inside OPEN cursor FOR SELECT... doesn't populate collection 【发布时间】:2016-08-17 10:17:13 【问题描述】:

我有一个过程,它将光标作为输出参数返回。在 SP 中,输出游标变量作为 SELECT 语句打开。我打算将此游标中的记录重用于 SP 中的以下逻辑,并使用 BULK COLLECT 子句将它们存储在嵌套表中。但发现,毫无例外,这个嵌套表没有被填充。 我写了一个简单的例子来说明这种行为:

create table temp_table as
select 1 as col1 from dual
union all
select 2 as col1 from dual;

declare
  v_cur sys_refcursor;
  v_rec temp_table%rowtype;
  procedure get_cursor(v_cur OUT sys_refcursor) is
    type typ_temp_tab_tab is table of temp_table%rowtype;
    v_tab typ_temp_tab_tab;
  begin
    v_tab:=typ_temp_tab_tab();
    open v_cur for 
      select * 
      bulk collect into v_tab
        from temp_table;
    dbms_output.put_line('nested table''s records num: '||v_tab.count);
  end;
begin
  get_cursor(v_cur);
  dbms_output.put_line('values in cursor: ');
  loop
    fetch v_cur into v_rec;
    exit when v_cur%NOTFOUND;
    dbms_output.put_line('>>'||v_rec.col1);   
  end loop;
end;
/
drop table temp_table;

输出是:

nested table's records num: 0
values in cursor: 
>>1
>>2

您知道为什么它不起作用吗?在 SP 中重用游标记录的最佳做法是什么?

【问题讨论】:

你不能两者都做。 (我很惊讶它没有抱怨这种语法)。如果不使用游标内容,您将无法获取它,因此如果您循环填充嵌套表,调用者将看不到游标中留下的任何内容。为什么两者都需要?嵌套表是否可以在模式级别声明,而不是作为本地 PL/SQL 类型? @AlexPoole,谢谢!是的,看起来是这样。如果不使用循环遍历它的游标,则无法填充嵌套表。我也想知道为什么这样的语法不会导致错误。我需要它,因为第一个我应该将游标返回到应用程序,第二个我需要使用相同的结果集来插入它。我可以将此查询包装在一个表函数中,但它看起来有点复杂,我需要实现独立的嵌套表类型。 如果您要创建一个独立的表类型,您只需执行一次查询 - 批量收集查询,然后针对该集合打开游标。 @AlexPoole 是的,我已经这样做了。我试图避免使用独立类型并两次使用相同的查询。再次感谢您的帮助! 【参考方案1】:

你不写:

open v_cur for 
      select * 
      bulk collect into v_tab
        from temp_table;

你只需要:

  select * 
  bulk collect into v_tab
    from temp_table;

没有“open v_cur for”。

不幸的是,我认为这意味着您不能既 (A) 将数据放在嵌套表中,又 (B) 在不运行两次查询的情况下将打开的游标返回给调用者。 Oracle 必须获取游标中的所有行才能执行BULK COLLECT,在此之后,游标将无法返回给调用者。

【讨论】:

谢谢!当我编写代码并且 Oracle 没有抱怨时,我很高兴我找到了同时执行 A 和 B 的方法。不幸的是我错了:)我将实现一个独立的表类型并重写这段代码。 如果您真的希望结果 (A) 填充到 PL/SQL 集合中并且 (B) 作为打开的游标传递回调用者,您可以在不先执行两次查询的情况下执行此操作正如我在回答中所展示的那样,将 BULK COLLECT 放入集合中,然后执行 OPEN v_cur FOR SELECT * FROM TABLE(temp_table)。但是,要使其正常工作,您需要在数据库中创建 typ_temp_tab_tabOBJECT。或者,如果您运行的是 12c,则可以在包规范中定义 TYPE 并使用它。 谢谢,我已经按照你描述的方式实现了。我希望我们都有 12c:)

以上是关于BULK COLLECT INTO inside OPEN cursor FOR SELECT... 不填充集合的主要内容,如果未能解决你的问题,请参考以下文章

Oracle bulk collect into 的几种用法

在 SELECT ... BULK COLLECT INTO 中使用 LIMIT 选项

使用 RETURNING INTO 子句和 BULK COLLECT 时如何返回整行

2015.1.15 利用Oracle函数插入表结构 Bulk collect into 不用循环,简洁高效

ORA-00947 当 UPDATE 返回 BULK COLLECT INTO 用户创建的 TYPE TABLE 时出现“没有足够的值”

ORACLEBulk Processing with BULK COLLECT and FORALL