Выбор значений из таблицы или переменной таблицы Oracle?
Следуя моему последнему вопросу (Табличные переменные в Oracle PL/SQL?)...
Как только у вас есть значения в массиве/таблице, как вы их снова получите? Предпочтительно использовать оператор выбора или что-то подобное?
Вот что у меня до сих пор:
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;
Я знаю, что могу вывести их с помощью dbms_output.putline(), но я надеюсь получить набор результатов, как если бы я мог выбирать из любой другой таблицы.
Спасибо заранее,
Matt
Ответы
Ответ 1
Возможно, вам понадобится ГЛОБАЛЬНАЯ ВРЕМЕННАЯ ТАБЛИЦА.
В Oracle они создаются один раз, а затем при вызове данные являются конфиденциальными для вашей сессии.
Ссылка на документацию Oracle
Попробуйте что-нибудь подобное...
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;
/
Ответ 2
В Oracle двигатели PL/SQL и SQL поддерживают некоторое разделение. Когда вы выполняете оператор SQL в PL/SQL, он передается движку SQL, который не знает структуры, специфичные для PL/SQL, такие как таблицы INDEX BY.
Итак, вместо объявления типа в блоке PL/SQL вам необходимо создать эквивалентный тип коллекции в схеме базы данных:
CREATE OR REPLACE TYPE array is table of number;
/
Затем вы можете использовать его, как в этих двух примерах в 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.
Дополнительный пример, основанный на комментариях
Основываясь на вашем комментарии к моему ответу и самому вопросу, я думаю, что именно так я и реализую его. Используйте пакет, чтобы записи можно было извлекать из фактической таблицы один раз и хранить в закрытом пакете global; и функция, которая возвращает открытый курсор 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;
/
Ответ 3
Тип массива sql не обязательно. Нет, если тип элемента является примитивным. (Varchar, число, дата,...)
Очень простой пример:
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;
Если вы хотите его повторно использовать, может быть интересно узнать, как это будет выглядеть.
Если вы выдаете несколько команд, то они могут быть сгруппированы в пакет.
У частного трейдера с переменной палитры есть свои недостатки.
Когда вы добавляете переменные в пакет, вы даете ему состояние, и теперь оно не действует как набор функций без состояния, а скорее как какой-то странный тип экземпляра объекта singleton.
например. Когда вы перекомпилируете тело, это вызовет исключения в сеансах, которые уже использовались ранее. (потому что значения переменных были недействительными)
Однако вы можете объявить тип в пакете (или глобально в sql) и использовать его в качестве параметра в методах, которые должны его использовать.
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;
Как это называется:
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;