2009-10-15 12 views
11

In seguito alla mia ultima domanda (Table Variables in Oracle PL/SQL?) ...Selezione dei valori da Oracle Table Variable/Array?

Una volta ottenuti i valori in un array/tabella, come si fa a tornare di nuovo? Preferibilmente usando un'istruzione select o qualcosa di simile?

Ecco quello che ho finora:

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; 

So che posso uscita utilizzando dbms_output.putline(), ma spero di ottenere un risultato impostato come se farebbe da selezione da qualsiasi altro tavolo.

Grazie in anticipo, Matt

+0

Um ... quale problema stai cercando di risolvere qui? Perché non esegui solo una selezione? –

+0

I valori che ho memorizzato nella tabella dei pidm verranno riutilizzati più volte in seguito durante l'elaborazione. I valori stessi richiedono un po 'di tempo per uscire dal database e quindi ho voluto memorizzarli in una posizione intermedia. Sto solo avendo problemi a recuperare i valori una volta che li ho inseriti ... –

risposta

14

Potrebbe essere necessaria una TABELLA TEMPORANEA GLOBALE.

In Oracle questi vengono creati una volta e quindi quando richiamati i dati sono privati ​​per la sessione.

Oracle Documentation Link

provare qualcosa di simile ...

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

Questo è un uso molto utile della tabella temporanea in Oracle. – jva

+2

Il collegamento è morto –

+0

La tabella temporanea globale deve essere creata durante la progettazione del database (al di fuori delle stored procedure, dei trigger, ...), quindi non può essere utilizzata in modo dinamico. Ma comunque mi risolve qualsiasi problema, perché nel mio caso non ne ho bisogno dinamicamente. –

10

In Oracle, PL/SQL e motori SQL mantenere una certa separazione. Quando si esegue un'istruzione SQL in PL/SQL, questa viene passata al motore SQL, che non ha alcuna conoscenza delle strutture specifiche di PL/SQL come le tabelle INDEX BY.

Così, invece di dichiarare il tipo nel blocco PL/SQL, è necessario creare un tipo di raccolta pari all'interno dello schema del database:

CREATE OR REPLACE TYPE array is table of number; 
/

Quindi è possibile utilizzarlo come in questi due esempi all'interno di 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. 

esempio addizionale sulla base dei commenti

basato sul tuo commento sulla mia risposta e sulla domanda stessa, penso che questo sia il modo in cui lo implementerei. Utilizzare un pacchetto in modo che i record possano essere recuperati dalla tabella effettiva una volta e memorizzati in un pacchetto privato globale; e avere una funzione che restituisce un cursore di riferimento aperto.

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

Grazie, Dave. Una domanda però ... in entrambi i tuoi esempi stai scorrendo la tabella p e produci il valore di ogni riga. Esiste comunque la possibilità di restituire tutti i valori senza loop? Equivalente SQL di "select * from p"? –

+0

C'è una varietà di cose che potresti fare. Ho aggiornato la mia risposta con un esempio di una funzione che restituisce un CURSORE DI RIF. –

0

Il tipo di matrice SQL non è necessario. Non se il tipo di elemento è primitivo. (Varchar, numero, data, ...)

campione molto semplice:

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; 

Quando si desidera riutilizzarlo, allora potrebbe essere interessante sapere come sarebbe simile. Se si emettono più comandi di quelli che potrebbero essere raggruppati in un pacchetto. Il trucco della variabile del pacchetto privato dall'alto ha i suoi lati negativi. Quando si aggiungono le variabili a un pacchetto, gli si fornisce lo stato e ora non si comporta come un gruppo di funzioni senza stato ma come una sorta di strana istanza di un oggetto Singleton.

ad es. Quando ricompili il corpo, solleverà eccezioni nelle sessioni che lo hanno già usato in precedenza.(perché i valori delle variabili sono stati invalidati)

Tuttavia, è possibile dichiarare il tipo in un pacchetto (o globalmente in sql) e utilizzarlo come parametro in metodi che dovrebbero utilizzarlo.

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; 

Come chiamarlo:

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; 
Problemi correlati