2009-10-15 16 views
11

Po od ostatniego pytania (Table Variables in Oracle PL/SQL?) ...Wybieranie wartości ze zmiennej tabeli Oracle?

Po uzyskaniu wartości w tablicy/tabeli, w jaki sposób odzyskać je ponownie? Najlepiej przy użyciu instrukcji select lub czegoś podobnego?

Oto co mam do tej pory:

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; 

wiem, że mogę im wyjście używając dbms_output.putline(), ale mam nadzieję, aby uzyskać wynik ustawić jak bym od wybierając z dowolnego inna tabela.

Dzięki z góry, Matt

+0

Um ... Jaki problem należy próbują rozwiązać tutaj? Dlaczego po prostu nie wybierzesz? –

+0

Wartości, które zapisałem w tabeli pidms, zostaną ponownie wykorzystane wielokrotnie podczas przetwarzania. Same wartości zabierają trochę czasu, aby wydostać się z bazy danych, dlatego chciałem przechowywać je w pośredniej lokalizacji. Mam problem z odzyskaniem wartości po ich wprowadzeniu ... –

Odpowiedz

14

Może być potrzebny GLOBALNY TABELA CZASOWĄ.

W Oracle są one tworzone raz, a następnie po wywołaniu dane są prywatne dla sesji.

Oracle Documentation Link

Spróbuj coś takiego ...

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

To bardzo ładne tymczasowe użycie tabel w Oracle. – jva

+2

Łącze jest martwe –

+0

Globalna tabela tymczasowa musi zostać utworzona podczas projektowania bazy danych (poza procedurami przechowywanymi, wyzwalaczami, ...), więc nie może być używana dynamicznie. Ale to rozwiązuje dla mnie jakiekolwiek problemy, ponieważ nie potrzebuję go dynamicznie w moim przypadku. –

10

W Oracle PL/SQL i silniki SQL zachować pewną separację. Po uruchomieniu instrukcji SQL w PL/SQL jest ona przekazywana do silnika SQL, który nie ma wiedzy o strukturach specyficznych dla PL/SQL, takich jak tabele INDEX BY.

Więc zamiast deklarowania typu w bloku PL/SQL, trzeba stworzyć odpowiednik typu kolekcji w ramach schematu bazy danych:

CREATE OR REPLACE TYPE array is table of number; 
/

Wtedy można go używać tak jak w tych dwóch przykładach ramach 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. 

dodatkowy przykład na podstawie uwag

oparte na twój komentarz na moją odpowiedź i na samym pytaniu, myślę, że w ten sposób zrealizuję to. Użyj pakietu, aby dane mogły zostać pobrane z rzeczywistej tabeli i zapisane w prywatnym pakiecie globalnym; i mają funkcję, która zwraca otwarty kursor ref.

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

Dzięki, Dave. Jedno pytanie jednak ... w obu przykładach przeplatasz tabelę p i wyprowadzasz wartość każdego wiersza. Czy mimo to zwraca wszystkie wartości bez pętli? Odpowiednik SQL "wybierz * z p"? –

+0

Istnieje wiele rzeczy, które możesz zrobić. Zaktualizowałem swoją odpowiedź na przykładzie funkcji zwracającej REF CURSOR. –

0

SQL typu tablica nie jest konieczne. Nie, jeśli typ elementu jest prymitywny. (Varchar, numer, data, ...)

Bardzo prosty przykład:

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; 

Gdy chcesz go ponownie użyć, to może być interesujące wiedzieć, jak to będzie wyglądać. Jeśli wydasz kilka poleceń, które mogą zostać zgrupowane w pakiecie. Sztuczna sztuczka pakietu z powyższej listy ma swoje wady. Gdy dodajesz zmienne do pakietu, nadajesz mu stan, a teraz nie działa on jako bezpaństwowy zestaw funkcji, ale zamiast tego jest jakaś dziwna instancja typu singleton.

np. Gdy przekompilujesz ciało, spowoduje to podniesienie wyjątków w sesjach, które już go używały.(ponieważ zmienne wartości zostały unieruchomione)

Można jednak zadeklarować typ w pakiecie (lub globalnie w sql) i użyć go jako parametru w metodach, które powinny z niego korzystać.

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; 

Jak nazwać:

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;