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

Posted

技术标签:

【中文标题】从 Oracle 表变量/数组中选择值?【英文标题】:Selecting Values from Oracle Table Variable / Array? 【发布时间】:2009-10-15 17:26:40 【问题描述】:

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

提前致谢, 马特

【问题讨论】:

嗯......你想在这里解决什么问题?为什么不直接运行一个选择? 我存储在 pidms 表中的值将在以后的处理过程中重复使用多次。这些值本身需要一些时间才能从数据库中取出,因此我想将它们存储在中间位置。一旦我把它们放进去,我就很难把它们找回来...... 【参考方案1】:

您可能需要一个全局临时表。

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

【讨论】:

这是Oracle中一个非常好的临时表使用。 全局临时表必须在数据库设计时创建(在存储过程、触发器等之外),因此不能动态使用。但是它为我解决了任何问题,因为在我的情况下我不需要动态地使用它。【参考方案2】:

在 Oracle 中,PL/SQL 和 SQL 引擎保持一定的分离。当您在 PL/SQL 中执行一条 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.

基于 cmets 的其他示例

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

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

【讨论】:

谢谢,戴夫。但是有一个问题...在您的两个示例中,您都在循环 p 表并输出每一行的值。无论如何要返回所有没有循环的值吗? SQL 等价于“select * from p”? 您可以做很多事情。我用一个返回 REF CURSOR 的函数示例更新了我的答案。【参考方案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;

如果您想重复使用它,那么了解它的样子可能会很有趣。 如果您发出多个命令,则这些命令可以组合在一个包中。 上面的私有包变量技巧有其缺点。 当你向一个包添加变量时,你给它一个状态,现在它不再充当一堆无状态的函数,而是充当某种奇怪的单例对象实例。

例如当您重新编译主体时,它会在之前已经使用过它的会话中引发异常。 (因为变量值无效)

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

【讨论】:

以上是关于从 Oracle 表变量/数组中选择值?的主要内容,如果未能解决你的问题,请参考以下文章

Oracle过程 - 选择所有表,然后循环遍历这些表的记录

Oracle APEX 选择列表

如何从具有字段名称作为默认关键字的 Oracle 表中选择值

从数组中选择一个随机值

oracle中的变量和多个选择语句

使用函数调用从选择中插入到表的 Oracle 中的性能