继我的上一个问题(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()输出它们,但我希望得到一个结果集,就像从其他任何表中选择一样。
提前致谢, 马特
答案 0 :(得分:15)
您可能需要一个GLOBAL TEMPORARY TABLE。
在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;
/
答案 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;