Oracle
 sql >> Datenbank >  >> RDS >> Oracle

Oracle FOR LOOP iteriert nicht in SYS_REFCURSOR

Beachten Sie die folgenden erweiterten Kommentare:

Vielleicht liegt der Kern der Frage in einem Missverständnis darüber, was ein Cursor ist. Es ist kein Container voller Datensätze, es ist eine Spezifikation für eine Ergebnismenge, wie zu einem bestimmten Zeitpunkt, basierend auf einer einzigen SQL-Abfrage. Wenn Sie also

open rc for select id from table1;

und übergeben Sie rc Zurück zum Aufrufer übergeben Sie keine Daten, sondern einen Zeiger auf einen privaten Speicherbereich, der eine vorbereitete Abfrage enthält. Sie pushen die Ergebnisse nicht, der Anrufer zieht sie. Es ist wie ein Programm, das der Aufrufer ausführt, um die Zeilen abzurufen. Sie können es nicht weiter öffnen, um eine weitere Zeile hinzuzufügen, was Sie meiner Meinung nach erhofft hatten.

Um eine Sammlung in einem Cursor innerhalb einer Prozedur zu verwenden, muss der Sammlungstyp als separates Schemaobjekt erstellt werden (obwohl Sie natürlich Sammlungstypen in anderen Prozeduren wiederverwenden können, also ist es nicht so restriktiv, wie es klingt).

Wenn Sie keinen Typ erstellen können, prüfen Sie, welche Typen bereits vorhanden sind und verwendet werden können:

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

Zum Beispiel:

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;

Jetzt kann die Prozedur sein:

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;

Cursorausgabe:

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

(Ich habe die i_id entfernt Parameter in Ihrer Prozedur, da mir nicht klar war, wie Sie ihn verwenden wollten.)

Vermutlich ist dies eine vereinfachte Version des eigentlichen Problems, denn so wie es aussieht, könnten Sie die erste Abfrage als Unterabfrage verwenden und würden die Sammlung nicht benötigen:

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;

oder machen Sie einfach mit, wie Littlefoot vorgeschlagen hat:

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;

Sie haben jedoch zu dieser Antwort kommentiert, dass Sie dies nicht tun könnten, da Ihre Anforderung zu sein schien, dies über eine Sammlung, eine Schleife, etwas Klebeband, zwei Katzen und einen Fusionsgenerator zu tun.