从Oracle表变量/数组中选择值?

时间:2009-10-15 17:26:41

标签: oracle plsql

继我的上一个问题(Table Variables in Oracle PL/SQL?)......

之后

一旦你有一个数组/表中的值,你如何让它们再次退出?最好是使用select语句还是其他类似的东西?

这是我到目前为止所得到的:

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()输出它们,但我希望得到一个结果集,就像从其他任何表中选择一样。

提前致谢, 马特

3 个答案:

答案 0 :(得分:15)

您可能需要一个GLOBAL TEMPORARY TABLE。

在Oracle中,这些创建一次,然后在调用时,数据对您的会话是私有的。

Oracle Documentation Link

尝试这样的事情......

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; 
/

答案 1 :(得分:11)

在Oracle中,PL / SQL和SQL引擎保持一定的分离。当您在PL / SQL中执行SQL语句时,它将被传递给SQL引擎,而SQL引擎不了解像INDEX BY表这样的PL / SQL特定结构。

因此,您需要在数据库模式中创建等效的集合类型,而不是在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.

基于评论的其他示例

根据您对我的答案和问题本身的评论,我认为这是我实施它的方式。使用包,以便可以从实际表中获取记录一次并存储在私有包全局中;并且有一个返回打开引用游标的函数。

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;
/

答案 2 :(得分:1)

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;

当你想重用它时,知道它会是什么样子可能会很有趣。 如果发出多个命令,则可以将这些命令分组到一个包中。 上面的私有包变量技巧有其缺点。 当你向包中添加变量时,你给它一个状态,现在它不是一组无状态函数,而是作为一些奇怪的单例对象实例。

e.g。当您重新编译正文时,它将在之前已经使用它的会话中引发异常。 (因为变量值无效)

但是,您可以在包中声明类型(或在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;