sql >> Base de Datos >  >> RDS >> Oracle

Oracle FOR LOOP no itera en SYS_REFCURSOR

Tenga en cuenta los siguientes comentarios ampliados:

Quizás en el centro de la pregunta hay un malentendido de lo que es un cursor. No es un contenedor lleno de registros, es una especificación para un conjunto de resultados, como en un momento dado, basado en una única consulta SQL. Así que si

open rc for select id from table1;

y pasar rc de vuelta a la persona que llama, no está pasando ningún dato, está pasando un puntero a un área de memoria privada que contiene una consulta preparada. Usted no presiona los resultados, la persona que llama los extrae. Es como un programa que la persona que llama ejecutará para obtener las filas. No puede abrirlo un poco más para agregar otra fila, que creo que es lo que esperaba hacer.

Para usar una colección en un cursor dentro de un procedimiento, el tipo de colección debe crearse como un objeto de esquema separado (aunque, por supuesto, puede reutilizar tipos de colección en otros procedimientos, por lo que no es tan restrictivo como parece).

Si no puede crear un tipo, vea qué tipos ya existen que puede usar:

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

Por ejemplo:

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;

Ahora el procedimiento puede ser:

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;

Salida del cursor:

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

(Quité el i_id parámetro en su procedimiento ya que no tenía claro cómo quería usarlo).

Presumiblemente, esta es una versión simplificada del problema real, porque tal como está, podría usar la primera consulta como una subconsulta y no necesitaría la colección:

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;

o simplemente únete, como sugirió 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;

Sin embargo, comentó en esa respuesta que no podía hacer eso porque su requisito parecía ser hacerlo a través de una colección, un bucle, cinta adhesiva, dos gatos y un generador de fusión.