【问题标题】:oracle FOR LOOP does not iterate in SYS_REFCURSORoracle FOR LOOP 不在 SYS_REFCURSOR 中迭代
【发布时间】:2021-03-12 18:09:29
【问题描述】:

程序如下:

  1. 打开游标,然后通过批量收集获取选择查询的输出。
  2. 问题是所有 ID 都存储在批量收集中,但我无法使用批量收集变量作为输入循环第二个选择查询,它只考虑第一个 ID 而不是全部。
  3. OUTPUT 应该是 SYS_REFCURSOR,请说明我在这里缺少什么

table1 的测试数据:

ID CURRENCY T_ID
10 GBP PB1
15 GBP RB
20 GBP CC
25 AUD DC

基于 t_id,我正在获取相应的 ID,然后在进一步的 select for 循环语句中使用这些 ID。

通过 SYS_REFCURSOR 的过程的当前输出:

ID COUNTRY ACCOUNT
10 UK PB1

预期输出:

ID COUNTRY ACCOUNT
10 UK PB1
15 Wales RB
20 SH CC
create or replace procedure myproc (i_id in varchar2, rc out sys_refcursor)
as
    cursor names_cur is
        select id from table1 where currency='GBP' and t_id=i_id;
    names_t names_cur%ROWTYPE;
    type names_ntt is table of names_t%TYPE;
    l_names names_ntt;
begin
    open names_cur;
    fetch names_cur bulk collect into l_names ; --Inside l_names (10,15 & 20) would be stored 
    close names_cur;
--iSSUE IS WITH BELOW FOR LOOP
    for cur in l_names.first..l_names.last loop
        open rc for --For the below select I want to iterate l_names so for the above scenario it should iterate thrice

        select s.id,s.country,s.account from table2 s where s.id=l_names(cur).id;
    end loop;

end myproc;

【问题讨论】:

  • 一些测试数据会很有帮助。我不清楚你需要这个程序做什么。例如,如果要返回游标,l_names 是什么?您只能生成带有查询的游标,并且只能打开一次。
  • 现在肯定会添加数据
  • 在您的过程中,您传递一个 ID 并仅从 table1 中选择具有该 ID 的行,但预期的输出包括多个 ID,所以我仍然不清楚您需要它做什么。
  • @William 当我将 i_id(来自 Proc)传递给 table1 中的 t_id(作为输入)时,根据输入,将根据连接条件获取多个匹配的 id,这就是您看到 3 的原因身份证。 1.所以基本上根据传递给过程的t_id获取所有ID的匹配项。 2. 将 ID 存储在一些变量/集合中 3. 在下一个选择查询中迭代 ID 作为输入 4. 将输出生成为 sys_refcursor 或数组

标签: oracle for-loop stored-procedures plsql bulk-collect


【解决方案1】:

注意以下扩展 cmets:

也许问题的中心是对光标是什么的误解。它不是一个装满记录的容器,它是一个结果集的规范,在某个时间点,基于单个 SQL 查询。所以如果你

open rc for select id from table1;

并将rc 传递回调用者,您没有传递任何数据,您传递的是指向包含准备好的查询的私有内存区域的指针。你不推结果,调用者拉他们。这就像调用者将执行以获取行的程序。你不能再打开它来添加另一行,我认为这是你希望做的。


要在过程中的游标中使用集合,必须将集合类型创建为单独的模式对象(当然,您可以在其他过程中重用集合类型,因此它不像听起来那么严格)。

如果您无法创建类型,请查看已经存在的可以使用的类型:

select owner, type_name
from   all_coll_types t
where  t.coll_type = 'TABLE'
and    t.elem_type_name = 'NUMBER';

例如:

create or replace type number_tt as table of number;

create table table1 (id primary key, currency, t_id) as
    select 10, 'GBP', 'PB1' from dual union all
    select 15, 'GBP', 'RB' from dual union all
    select 20, 'GBP', 'CC' from dual union all
    select 25, 'AUD', 'DC' from dual;

create table table2 (id,country,account) as
    select 10, 'UK', 'PB1' from dual union all
    select 15, 'Wales', 'RB' from dual union all
    select 20, 'SH', 'CC' from dual;

现在程序可以是:

create or replace procedure myproc
    ( rc out sys_refcursor)
as
    l_names number_tt;
begin
    select id bulk collect into l_names
    from   table1
    where  currency = 'GBP';

    open rc for
        select t.id,t.country,t.account from table2 t
        where  t.id member of l_names;
end myproc;

光标输出:

        ID COUNT ACC
---------- ----- ---
        10 UK    PB1
        15 Wales RB
        20 SH    CC

(我在您的过程中删除了i_id 参数,因为我不清楚您想如何使用它。)

大概这是实际问题的简化版本,因为就目前而言,您可以将第一个查询用作子查询,而您不需要集合:

create or replace procedure myproc
    ( rc out sys_refcursor)
as
begin
    open rc for
        select t.id,t.country,t.account from table2 t
        where  t.id in
               ( select id 
                 from   table1
                 where  currency = 'GBP' );
end myproc;

或者按照 Littlefoot 的建议加入它:

create or replace procedure myproc
    ( rc out sys_refcursor)
as
begin
    open rc for
        select t2.id, t2.country, t2.account
        from   table1 t1
               join table2 t2 on t2.id = t1.id
        where  t1.currency = 'GBP';
end myproc;

但是,您对那个答案的评论是您不能这样做,因为您的要求似乎是通过一个集合、一个循环、一些胶带、两只猫和一个融合发生器来做到这一点。

【讨论】:

  • 为我工作:dbfiddle.uk/…
  • 道歉威廉添加了一个额外的;在我的代码中。但是,我没有运行create or replace type number_tt as table of number; 的特权,DBA 似乎有一些限制。介意检查为什么带有 SYS_REFCURSOR 的 for 循环不迭代吗?如果解决了,那么我的问题就会得到解答
  • @Linnea - 循环确实会迭代。在每次迭代中,您都会打开一个新游标,该游标会隐式丢弃先前打开的游标。所以当过程返回时,只返回最近打开的游标。
  • 这就是我回答的重点。不要为集合中的每个值打开游标,一次一个值。只打开一次,通过整个集合。我使用了member of,但其他语法变体也可以使用(加入,in (subquery))。
  • 输出什么? rc 是一个引用光标。它表示一组零个或多个行。您将返回一个引用光标rc。您必须从查询中打开它,您不能增量构建它。迭代与它有什么关系?
【解决方案2】:

你需要光标做什么?还有过程的IN参数(因为你没用过)?

无论如何:

create or replace procedure myproc (i_id in varchar2, rc out sys_refcursor)
  as
begin
  open rc
    select t.id,
           t.country,
           t.account 
    from table2 t join table1 a on a.id = t.id
    where a.currency = 'GBP';
end;

【讨论】:

  • 好的,但是 - 正如你所看到的 - 它只是行不通。 RC 返回了您的 L_NAMES 中返回的第一个 ID,仅此而已。不管你要加入多少表,Oracle 都能做到。
  • 如果你必须这样做,那么将返回值存储到一个集合中 - 随着代码的进行逐个添加 - 并返回它(集合)而不是 refcursor。
  • 但是,你已经做到了,L_NAMES 是一个集合。创建一个(在 SQL 级别)将保存您想要返回的结果。
  • 我的意思是,CREATE TYPE 在 SQL 级别,而不是在过程中,否则您将无法使用此过程的结果,这就是您计划做的,对吧?为什么?因为您要从程序中返回结果!
  • 提供的解决方案无法回答我的问题,如果您查看提供的代码会有所帮助
猜你喜欢
  • 2016-09-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-17
  • 1970-01-01
  • 2021-06-06
  • 2017-01-19
  • 2019-08-23
相关资源
最近更新 更多