2009-10-15 32 views
11

A continuación de mi última pregunta (Table Variables in Oracle PL/SQL?) ...¿Seleccionar valores de la tabla de Oracle Variable/Array?

Una vez que tenga los valores en una matriz/tabla, ¿cómo los puede volver a sacar? Preferiblemente usando una declaración de selección o algo por el estilo?

Esto es lo que tengo hasta ahora:

declare 
    type array is table of number index by binary_integer; 
    pidms array; 
begin 
    for i in (
       select distinct sgbstdn_pidm 
       from sgbstdn 
       where sgbstdn_majr_code_1 = 'HS04' 
       and sgbstdn_program_1 = 'HSCOMPH' 
       ) 
    loop 
     pidms(pidms.count+1) := i.sgbstdn_pidm; 
    end loop; 

    select * 
    from pidms; --ORACLE DOESN'T LIKE THIS BIT!!! 
end; 

Sé que puedo salida de ellos, usando dbms_output.putline(), pero estoy esperando para obtener un conjunto de resultados como lo haría desde la selección de cualquier otra mesa

Gracias de antemano, Matt

+0

Um ... ¿qué problema está tratando de resolver aquí? ¿Por qué no solo ejecutas un seleccionar? –

+0

Los valores que he almacenado en la tabla de pidms van a ser reutilizados varias veces más tarde en mi procesamiento. Los valores en sí tardan un poco en salir de la base de datos, por lo que quería almacenarlos en una ubicación intermedia. Solo estoy teniendo problemas para recuperar los valores una vez que los puse en ... –

Respuesta

14

Es posible que necesite una MESA TEMPORAL GLOBAL.

En Oracle estos se crean una vez y cuando se invoca, los datos son privados para su sesión.

Oracle Documentation Link

Pruebe algo como esto ...

CREATE GLOBAL TEMPORARY TABLE temp_number 
    (number_column NUMBER(10, 0) 
    ) 
    ON COMMIT DELETE ROWS; 

BEGIN 
    INSERT INTO temp_number 
     (number_column) 
     (select distinct sgbstdn_pidm 
      from sgbstdn 
     where sgbstdn_majr_code_1 = 'HS04' 
      and sgbstdn_program_1 = 'HSCOMPH' 
    ); 

    FOR pidms_rec IN (SELECT number_column FROM temp_number) 
    LOOP 
     -- Do something here 
     NULL; 
    END LOOP; 
END; 
/
+0

Este es un muy buen uso de tabla temporal en Oracle. – jva

+2

El enlace está muerto –

+0

La tabla temporal global debe crearse durante el diseño de la base de datos (fuera de los procedimientos almacenados, disparadores, ...), por lo que no se puede usar dinámicamente. Pero, sin embargo, me resuelve cualquier problema, porque no lo necesito dinámicamente en mi caso. –

10

En Oracle, PL/SQL y motores SQL mantienen cierta separación. Cuando ejecuta una instrucción SQL dentro de PL/SQL, se transfiere al motor SQL, que no tiene conocimiento de estructuras específicas de PL/SQL como las tablas INDEX BY.

Así, en lugar de declarar el tipo en el bloque PL/SQL, es necesario crear un tipo de colección equivalente en el esquema de base de datos:

CREATE OR REPLACE TYPE array is table of number; 
/

A continuación, puede usarlo como en estos dos ejemplos dentro PL/SQL:

SQL> l 
    1 declare 
    2 p array := array(); 
    3 begin 
    4 for i in (select level from dual connect by level < 10) loop 
    5  p.extend; 
    6  p(p.count) := i.level; 
    7 end loop; 
    8 for x in (select column_value from table(cast(p as array))) loop 
    9  dbms_output.put_line(x.column_value); 
10 end loop; 
11* end; 
SQL>/
1 
2 
3 
4 
5 
6 
7 
8 
9 

PL/SQL procedure successfully completed. 

SQL> l 
    1 declare 
    2 p array := array(); 
    3 begin 
    4 select level bulk collect into p from dual connect by level < 10; 
    5 for x in (select column_value from table(cast(p as array))) loop 
    6  dbms_output.put_line(x.column_value); 
    7 end loop; 
    8* end; 
SQL>/
1 
2 
3 
4 
5 
6 
7 
8 
9 

PL/SQL procedure successfully completed. 

ejemplo adicional basado en los comentarios

en base a su comentario en mi respuesta y sobre la pregunta en sí, creo que así es como lo implementaría. Use un paquete para que los registros se puedan obtener de la tabla real una vez y se almacenen en un paquete privado global; y tiene una función que devuelve un cursor de ref abierta.

CREATE OR REPLACE PACKAGE p_cache AS 
    FUNCTION get_p_cursor RETURN sys_refcursor; 
END p_cache; 
/

CREATE OR REPLACE PACKAGE BODY p_cache AS 

    cache_array array; 

    FUNCTION get_p_cursor RETURN sys_refcursor IS 
    pCursor sys_refcursor; 
    BEGIN 
    OPEN pCursor FOR SELECT * from TABLE(CAST(cache_array AS array)); 
    RETURN pCursor; 
    END get_p_cursor; 

    -- Package initialization runs once in each session that references the package 
    BEGIN 
    SELECT level BULK COLLECT INTO cache_array FROM dual CONNECT BY LEVEL < 10; 
    END p_cache; 
/
+0

Gracias, Dave. Una pregunta, sin embargo ... en ambos ejemplos, estás recorriendo la tabla p y generando el valor de cada fila. ¿Hay alguna forma de devolver todos los valores sin bucle? SQL equivalente a "select * from p"? –

+0

Hay una variedad de cosas que podrías hacer. Actualicé mi respuesta con un ejemplo de una función que devuelve un REF CURSOR. –

0

El SQL tipo de matriz no es necesario. No si el tipo de elemento es primitivo. (Varchar, número, fecha, ...)

ejemplo muy básico:

declare 
    type TPidmList is table of sgbstdn.sgbstdn_pidm%type; 
    pidms TPidmList; 
begin 
    select distinct sgbstdn_pidm 
    bulk collect into pidms 
    from sgbstdn 
    where sgbstdn_majr_code_1 = 'HS04' 
    and sgbstdn_program_1 = 'HSCOMPH'; 

    -- do something with pidms 

    open :someCursor for 
    select value(t) pidm 
    from table(pidms) t; 
end; 

Cuando desee volver a utilizarlo, entonces podría ser interesante saber cómo se vería así. Si emite varios comandos, estos se pueden agrupar en un paquete. El truco de variable de paquete privado de arriba tiene sus desventajas. Cuando agrega variables a un paquete, le da el estado y ahora no actúa como un conjunto de funciones sin estado, sino como un tipo extraño de instancia de objeto singleton en su lugar.

p. Ej. Cuando recompila el cuerpo, levantará excepciones en sesiones que ya lo usaron antes.(porque los valores de la variable se invalidaron)

Sin embargo, puede declarar el tipo en un paquete (o globalmente en sql) y usarlo como un parámetro en los métodos que deben usarlo.

create package Abc as 
    type TPidmList is table of sgbstdn.sgbstdn_pidm%type; 

    function CreateList(majorCode in Varchar, 
         program in Varchar) return TPidmList; 

    function Test1(list in TPidmList) return PLS_Integer; 
    -- "in" to make it immutable so that PL/SQL can pass a pointer instead of a copy 
    procedure Test2(list in TPidmList); 
end; 

create package body Abc as 

    function CreateList(majorCode in Varchar, 
         program in Varchar) return TPidmList is 
    result TPidmList; 
    begin 
    select distinct sgbstdn_pidm 
    bulk collect into result 
    from sgbstdn 
    where sgbstdn_majr_code_1 = majorCode 
    and sgbstdn_program_1 = program; 

    return result; 
    end; 

    function Test1(list in TPidmList) return PLS_Integer is 
    result PLS_Integer := 0; 
    begin 
    if list is null or list.Count = 0 then 
     return result; 
    end if; 

    for i in list.First .. list.Last loop 
     if ... then 
     result := result + list(i); 
     end if; 
    end loop; 
    end; 

    procedure Test2(list in TPidmList) as 
    begin 
    ... 
    end; 

    return result; 
end; 

cómo llamarlo:

declare 
    pidms constant Abc.TPidmList := Abc.CreateList('HS04', 'HSCOMPH'); 
    xyz PLS_Integer; 
begin 
    Abc.Test2(pidms); 
    xyz := Abc.Test1(pidms); 
    ... 

    open :someCursor for 
    select value(t) as Pidm, 
      xyz as SomeValue 
    from table(pidms) t; 
end; 
Cuestiones relacionadas