在 Oracle10gv2 中使用 sys_refcursor 发送嵌套的 table
Send a nested table using sys_refcursor in Oracle10gv2
我需要使用 sys_refcursor 将一些数据发送到 Jasper Reports。此数据是 pl/sql 中查询的结果和对查询结果的评估。这个想法是计算在相同 table 中由多个列过滤的一些值,并且由于过滤限制,这不能在具有 subselect 的查询中完成。抱歉不是很清楚,但我有保密协议。但是,我可以 post 一些代码并解释我必须实现的功能的重要部分。该项目基于Java,使用Oracle 10gv2 和JasperReports 3.6.1,无法更新(因此没有Oracle v12)。
我有一个关联数组的过程,其中填充了键和我必须 return 的值。键表示与目标报表上每个列类型关联的过滤结果,值是必须填充正确列的数字。这是关联数组的过程创建和声明。
create or replace PROCEDURE test_proc02(test_cursor OUT sys_refcursor) IS
/* associative arrays declaration */
TYPE transfer_type IS TABLE OF NUMBER
INDEX BY VARCHAR2(10);
transfer_table transfer_type;
但问题之一是我不能像这样使用具有 sys_refcursor 的关联数组:
Select * from table(cast(transfer_table AS transfer_type))
所以我将关联数组值复制到嵌套 Table 中,相信之前的 select 将适用于该结构。这是部分代码
/* nested table declaration */
TYPE transfer_nt_type IS TABLE OF VARCHAR2(20);
/* nested table initilization */
transfer_nt transfer_nt_type := transfer_nt_type();
/* some variables */
transfer_id VARCHAR2(10);
transfer_number NUMBER;
nt_counter INTEGER := 0;
nt_iter VARCHAR2(10);
/* copying AA into NT */
nt_iter := transfer_table.FIRST;
WHILE (nt_iter IS NOT NULL)
LOOP
nt_counter := nt_counter+1;
transfer_nt.EXTEND;
transfer_nt(nt_counter):=transfer_table(nt_iter);
dbms_output.put_line('nested table ('||nt_counter||'): '||transfer_nt(nt_counter));
nt_iter := transfer_table.NEXT(nt_iter);
END LOOP;
/* Trying to send NT to JR */
OPEN travelCursor FOR SELECT * FROM TABLE(cast(transfer_nt AS transfer_nt_type));
/* ERROR */
PLS-00382: expression is of wrong type
我不关心方法,我只是想将数据发送到 JR 以生成报告,所以如果我必须替换完整的 Procedure 结构,我也可以。我在 Whosebug 和其他资源中搜索了几天,但似乎没有任何效果,所以我不确定我的所有概念想法是否有误。
有什么想法吗?谢谢。
编辑:
transfer_nt_type 的类型声明错误,从以前的版本复制而来。现在是正确的。 AA的数据是这样的:
Key value
--------------
A548521 5
A325411 12
A329471 9
对的总数为 32,键为 varchar2(10),值为数字。最后嵌套的table的内容(VARCHAR(20))为:
A548521#5,A325411#12,A329471#9
类型在架构级别声明。我也试过:
OPEN travelCursor FOR
SELECT CAST(MULTISET(
SELECT * FROM TABLE(transfer_nt)
ORDER BY 1) AS transfer_nt_type)
INTO transfer_nt_out FROM DUAL;
结果相同。两种数据结构均已通过 dbms_output 完美测试和打印,结构中的数据是正确的。如果可能的话,我至少需要按照给定的顺序发送这些值。如果我可以在值响应中保持一定的顺序,则键并不重要。
已编辑以反映 Alex Poole 提案。在程序开始之前:
FUNCTION transfer_func (transfer_table transfer_type)RETURN transfer_nt_type PIPELINED IS
--TYPE transfer_type IS TABLE OF NUMBER INDEX BY VARCHAR2(10);
--transfer_table transfer_type;
nt_iter VARCHAR2(10);
BEGIN
nt_iter := transfer_table.FIRST;
WHILE (nt_iter IS NOT NULL)
LOOP
PIPE ROW (nt_iter || '#' || transfer_table(nt_iter));
nt_iter := transfer_table.NEXT(nt_iter);
END LOOP;
END transfer_func;
程序结束前:
OPEN travelCursor for select * from table(transfer_func(transfer_table));
同样的错误:
PLS-00382: expression is of wrong type
最终编辑和解决方案:
最后我用GTT解决了这个问题。我不知道为什么,但我第一次尝试这种方法时,Oracle Developer return 出现了与其他可能的解决方案相同的错误。我尝试了最古老的方法:关闭程序,重置机器并从头开始·而且奏效了!当然,只有 GTT。
nt_iter := transfer_table.FIRST;
WHILE (nt_iter IS NOT NULL)
LOOP
nt_counter := nt_counter+1;
INSERT INTO transfer_temp VALUES(nt_iter,transfer_table(nt_iter),06);
nt_iter := transfer_table.NEXT(nt_iter);
END LOOP;
OPEN test_cursor FOR select * from transfer_temp order by transfer_temp.id;
CREATE GLOBAL TEMPORARY TABLE transfer_temp (
id VARCHAR(20),
value NUMBER,
month NUMBER
)
ON COMMIT PRESERVE ROWS;
感谢大家的帮助!
一个(也许是低性能)简单的解决方案是将结果写入临时 table,然后
OPEN travelCursor FOR SELECT * FROM That_Temp_Table;
您可以改用流水线函数,使用模式级 table 类型:
create or replace TYPE transfer_nt_type AS TABLE OF VARCHAR2(20)
/
create or replace FUNCTION test_func02
RETURN transfer_nt_type PIPELINED IS
TYPE transfer_type IS TABLE OF NUMBER INDEX BY VARCHAR2(10);
transfer_table transfer_type;
nt_iter VARCHAR2(10);
BEGIN
-- sample data
transfer_table('A548521') := 5;
transfer_table('A325411') := 12;
transfer_table('A329471') := 9;
nt_iter := transfer_table.FIRST;
WHILE (nt_iter IS NOT NULL)
LOOP
PIPE ROW (nt_iter || '#' || transfer_table(nt_iter));
nt_iter := transfer_table.NEXT(nt_iter);
END LOOP;
RETURN;
END test_func02;
/
那么你可以这样做:
select * from table(test_func02);
得到:
Result Sequence
--------------------
A325411#12
A329471#9
A548521#5
如果您仅限于引用游标,那么您可以添加一个包装程序:
create or replace PROCEDURE test_proc02(test_cursor OUT sys_refcursor) IS
BEGIN
OPEN test_cursor FOR SELECT * FROM TABLE(test_func02);
END test_proc02;
/
var rc refcursor;
exec test_proc02(:rc);
print rc;
Result Sequence
--------------------
A325411#12
A329471#9
A548521#5
我相信你有问题,因为:
1) You defined your type inside procedure
所以尝试像上面的例子一样创建类型
CREATE OR REPLACE TYPE "TSTRINGTABLE" AS TABLE OF VARCHAR2(20)
/
2) You use select * from on indexed table...
如果您需要在嵌套 table 中存储 2 个值,则创建 table 类型的记录,其中的记录类似于
CREATE OR REPLACE TYPE "TSTRING2LIST_RECORD" AS OBJECT
(
column1_value VARCHAR2(4000),
column2_value VARCHAR2(4000)
)
/
CREATE OR REPLACE TYPE "TSTRING2LIST_TABLE" IS TABLE OF TSTRING2LIST_RECORD
/
在那之后你可以在你的程序中声明它并随心所欲地使用它,我下面的代码可能会被破坏,因为我写的时候没有 IDE,但是你需要理解我的想法:
PROCEDURE idk(cur out sys_refcursor) IS
l_table tstring2list_table := tstring2list_table();
BEGIN
l_table.extend;
l_table(l_table.count) := tstring2list_record('idx1', 'value1');
l_table.extend;
l_table(l_table.count) := tstring2list_record('idx2', 'value2');
l_table.extend;
l_table(l_table.count) := tstring2list_record('idx3', 'value3');
OPEN cur for select column2_value from table(l_table);
END;
您还可以使用批量收集或动态 SQL 或其他任何方式:
SELECT tstring2list_record(t.col1, t.col2) BULK COLLECT
INTO l_table
FROM some_table t
我需要使用 sys_refcursor 将一些数据发送到 Jasper Reports。此数据是 pl/sql 中查询的结果和对查询结果的评估。这个想法是计算在相同 table 中由多个列过滤的一些值,并且由于过滤限制,这不能在具有 subselect 的查询中完成。抱歉不是很清楚,但我有保密协议。但是,我可以 post 一些代码并解释我必须实现的功能的重要部分。该项目基于Java,使用Oracle 10gv2 和JasperReports 3.6.1,无法更新(因此没有Oracle v12)。
我有一个关联数组的过程,其中填充了键和我必须 return 的值。键表示与目标报表上每个列类型关联的过滤结果,值是必须填充正确列的数字。这是关联数组的过程创建和声明。
create or replace PROCEDURE test_proc02(test_cursor OUT sys_refcursor) IS
/* associative arrays declaration */
TYPE transfer_type IS TABLE OF NUMBER
INDEX BY VARCHAR2(10);
transfer_table transfer_type;
但问题之一是我不能像这样使用具有 sys_refcursor 的关联数组:
Select * from table(cast(transfer_table AS transfer_type))
所以我将关联数组值复制到嵌套 Table 中,相信之前的 select 将适用于该结构。这是部分代码
/* nested table declaration */
TYPE transfer_nt_type IS TABLE OF VARCHAR2(20);
/* nested table initilization */
transfer_nt transfer_nt_type := transfer_nt_type();
/* some variables */
transfer_id VARCHAR2(10);
transfer_number NUMBER;
nt_counter INTEGER := 0;
nt_iter VARCHAR2(10);
/* copying AA into NT */
nt_iter := transfer_table.FIRST;
WHILE (nt_iter IS NOT NULL)
LOOP
nt_counter := nt_counter+1;
transfer_nt.EXTEND;
transfer_nt(nt_counter):=transfer_table(nt_iter);
dbms_output.put_line('nested table ('||nt_counter||'): '||transfer_nt(nt_counter));
nt_iter := transfer_table.NEXT(nt_iter);
END LOOP;
/* Trying to send NT to JR */
OPEN travelCursor FOR SELECT * FROM TABLE(cast(transfer_nt AS transfer_nt_type));
/* ERROR */
PLS-00382: expression is of wrong type
我不关心方法,我只是想将数据发送到 JR 以生成报告,所以如果我必须替换完整的 Procedure 结构,我也可以。我在 Whosebug 和其他资源中搜索了几天,但似乎没有任何效果,所以我不确定我的所有概念想法是否有误。
有什么想法吗?谢谢。
编辑:
transfer_nt_type 的类型声明错误,从以前的版本复制而来。现在是正确的。 AA的数据是这样的:
Key value
--------------
A548521 5
A325411 12
A329471 9
对的总数为 32,键为 varchar2(10),值为数字。最后嵌套的table的内容(VARCHAR(20))为:
A548521#5,A325411#12,A329471#9
类型在架构级别声明。我也试过:
OPEN travelCursor FOR
SELECT CAST(MULTISET(
SELECT * FROM TABLE(transfer_nt)
ORDER BY 1) AS transfer_nt_type)
INTO transfer_nt_out FROM DUAL;
结果相同。两种数据结构均已通过 dbms_output 完美测试和打印,结构中的数据是正确的。如果可能的话,我至少需要按照给定的顺序发送这些值。如果我可以在值响应中保持一定的顺序,则键并不重要。
已编辑以反映 Alex Poole 提案。在程序开始之前:
FUNCTION transfer_func (transfer_table transfer_type)RETURN transfer_nt_type PIPELINED IS
--TYPE transfer_type IS TABLE OF NUMBER INDEX BY VARCHAR2(10);
--transfer_table transfer_type;
nt_iter VARCHAR2(10);
BEGIN
nt_iter := transfer_table.FIRST;
WHILE (nt_iter IS NOT NULL)
LOOP
PIPE ROW (nt_iter || '#' || transfer_table(nt_iter));
nt_iter := transfer_table.NEXT(nt_iter);
END LOOP;
END transfer_func;
程序结束前:
OPEN travelCursor for select * from table(transfer_func(transfer_table));
同样的错误:
PLS-00382: expression is of wrong type
最终编辑和解决方案:
最后我用GTT解决了这个问题。我不知道为什么,但我第一次尝试这种方法时,Oracle Developer return 出现了与其他可能的解决方案相同的错误。我尝试了最古老的方法:关闭程序,重置机器并从头开始·而且奏效了!当然,只有 GTT。
nt_iter := transfer_table.FIRST;
WHILE (nt_iter IS NOT NULL)
LOOP
nt_counter := nt_counter+1;
INSERT INTO transfer_temp VALUES(nt_iter,transfer_table(nt_iter),06);
nt_iter := transfer_table.NEXT(nt_iter);
END LOOP;
OPEN test_cursor FOR select * from transfer_temp order by transfer_temp.id;
CREATE GLOBAL TEMPORARY TABLE transfer_temp (
id VARCHAR(20),
value NUMBER,
month NUMBER
)
ON COMMIT PRESERVE ROWS;
感谢大家的帮助!
一个(也许是低性能)简单的解决方案是将结果写入临时 table,然后
OPEN travelCursor FOR SELECT * FROM That_Temp_Table;
您可以改用流水线函数,使用模式级 table 类型:
create or replace TYPE transfer_nt_type AS TABLE OF VARCHAR2(20)
/
create or replace FUNCTION test_func02
RETURN transfer_nt_type PIPELINED IS
TYPE transfer_type IS TABLE OF NUMBER INDEX BY VARCHAR2(10);
transfer_table transfer_type;
nt_iter VARCHAR2(10);
BEGIN
-- sample data
transfer_table('A548521') := 5;
transfer_table('A325411') := 12;
transfer_table('A329471') := 9;
nt_iter := transfer_table.FIRST;
WHILE (nt_iter IS NOT NULL)
LOOP
PIPE ROW (nt_iter || '#' || transfer_table(nt_iter));
nt_iter := transfer_table.NEXT(nt_iter);
END LOOP;
RETURN;
END test_func02;
/
那么你可以这样做:
select * from table(test_func02);
得到:
Result Sequence
--------------------
A325411#12
A329471#9
A548521#5
如果您仅限于引用游标,那么您可以添加一个包装程序:
create or replace PROCEDURE test_proc02(test_cursor OUT sys_refcursor) IS
BEGIN
OPEN test_cursor FOR SELECT * FROM TABLE(test_func02);
END test_proc02;
/
var rc refcursor;
exec test_proc02(:rc);
print rc;
Result Sequence
--------------------
A325411#12
A329471#9
A548521#5
我相信你有问题,因为:
1) You defined your type inside procedure
所以尝试像上面的例子一样创建类型
CREATE OR REPLACE TYPE "TSTRINGTABLE" AS TABLE OF VARCHAR2(20)
/
2) You use select * from on indexed table...
如果您需要在嵌套 table 中存储 2 个值,则创建 table 类型的记录,其中的记录类似于
CREATE OR REPLACE TYPE "TSTRING2LIST_RECORD" AS OBJECT
(
column1_value VARCHAR2(4000),
column2_value VARCHAR2(4000)
)
/
CREATE OR REPLACE TYPE "TSTRING2LIST_TABLE" IS TABLE OF TSTRING2LIST_RECORD
/
在那之后你可以在你的程序中声明它并随心所欲地使用它,我下面的代码可能会被破坏,因为我写的时候没有 IDE,但是你需要理解我的想法:
PROCEDURE idk(cur out sys_refcursor) IS
l_table tstring2list_table := tstring2list_table();
BEGIN
l_table.extend;
l_table(l_table.count) := tstring2list_record('idx1', 'value1');
l_table.extend;
l_table(l_table.count) := tstring2list_record('idx2', 'value2');
l_table.extend;
l_table(l_table.count) := tstring2list_record('idx3', 'value3');
OPEN cur for select column2_value from table(l_table);
END;
您还可以使用批量收集或动态 SQL 或其他任何方式:
SELECT tstring2list_record(t.col1, t.col2) BULK COLLECT
INTO l_table
FROM some_table t