Выбор значений из таблицы или переменной таблицы 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;