从 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 表变量/数组中选择值?的主要内容,如果未能解决你的问题,请参考以下文章