函数返回 Sys_refcursor
Function retuning Sys Refcursor
如何在 select 语句中调用返回 sys refcursor 的函数。我创建了一个这样的函数,我想调用 select 语句返回来自函数的两个值。所以我在这样的查询中使用,但它返回游标代替列值。
Function HCLT_GET_TASK_DATES(i_ownerid IN NUMBER, i_itemid IN NUMBER)
RETURN SYS_REFCURSOR IS
o_DATACUR SYS_REFCURSOR;
begin
open o_DATACUR for
select nvl(TO_CHAR(min(pref_start), 'DD-MON-YYYY'), '') AS MIN_DATE,
nvl(TO_CHAR(max(pref_finish), 'DD-MON-YYYY'), '') AS MAX_DATE
from autoplanallocation
WHERE project_id = i_ownerid
AND task_id = i_itemid;
RETURN o_DATACUR;
END;
/
SELECT HCLT_GET_TASK_DATES(267157, 15334208),
tv.taskid,
tv.wbs_code AS wbscode,
tv.taskcode,
tv.act_name,
ltrim(regexp_replace(tv.stageactorlovs, '[^#]*#(\d+?),', ','), ',') as stageactorlovs,
tv.createdat,
tv.pushedtoTaskModule,
tv.OVERALLSTATUS AS overallstatus1,
tv.ACTIVITY_CODE_ID,
tv.wbs_code,
TO_CHAR(tv.pref_st, 'DD-MON-YYYY') AS pref_st,
TO_CHAR(tv.pref_fn, 'DD-MON-YYYY') AS pref_fn,
tv.ACTL_EFFORT,
tv.rollup_effort,
tv.overAllStatus,
tv.FIELD5,
tv.FIELD4,
tv.activity_code_id
FROM task_view tv, autoplanallocation al
WHERE al.project_id = tv.ownerid(+)
and al.task_id = tv.taskid(+)
and tv.ownertype = 'Prj'
AND tv.ownerid = 267157
AND (tv.overAllStatus = 'All' OR 'All' = 'All')
AND (TaskId IN
((SELECT xyz
FROM (SELECT ToItemID xyz
FROM ItemTraceability it
WHERE it.FromOwnerType = 'Prj'
AND it.FromOwnerID = 267157
AND it.FromItemType = it.FromItemType
AND it.FromChildItemType = 'USTRY'
AND it.FromItemID = 15334208
AND it.ToOwnerType = 'Prj'
AND it.ToOwnerID = 267157
AND it.ToItemType = it.ToItemType
AND it.ToChildItemType = 'Tsk'
UNION ALL
SELECT FromItemID
FROM ItemTraceability it
WHERE it.ToOwnerType = 'Prj'
AND it.ToOwnerID = 267157
AND it.ToItemType = it.ToItemType
AND it.ToChildItemType = 'USTRY'
AND it.ToItemID = 15334208
AND it.FromOwnerType = 'Prj'
AND it.FromOwnerID = 267157
AND it.FromItemType = it.FromItemType
AND it.FromChildItemType = 'Tsk'))))
ORDER BY UPPER(wbs_code) ASC;
我认为没有使用 SQL 或 PL/SQL 代码解析嵌套游标的本机方法。
在 Java 中使用 Oracle JDBC 数据库驱动程序,您可以:
- 使用
oracle.jdbc.driver.OraclePreparedStatement.executeQuery
得到一个java.sql.ResultSet
- 可以转换为
oracle.jdbc.driver.OracleResultSet
- 然后您可以遍历结果集的行,对于每一行,您可以使用
oracle.jdbc.driver.OracleResultSet.getCursor()
来获取嵌套游标。
- 然后,您可以使用与迭代外部游标完全相同的方式来迭代该嵌套游标,以从嵌套游标中提取行。
- 然后您应该关闭嵌套游标(尽管它会在包含父游标关闭时自动关闭)。
- 最后,关闭父游标。
如果您想要 SQL 解决方案,则不要 return 游标和 return 嵌套 table 集合数据类型。
或者,对于具有多列的单行,return一个对象类型:
CREATE TYPE date_range_obj AS OBJECT(
start_date DATE,
end_date DATE
)
/
CREATE FUNCTION HCLT_GET_TASK_DATES(
i_ownerid IN autoplanallocation.project_id%TYPE,
i_itemid IN autoplanallocation.task_id%TYPE
)
RETURN date_range_obj
IS
v_range date_range_obj;
begin
SELECT date_range_obj(MIN(pref_start), MAX(pref_finish))
INTO v_range
FROM autoplanallocation
WHERE project_id = i_ownerid
AND task_id = i_itemid;
RETURN v_range;
END;
/
然后,例如:
SELECT HCLT_GET_TASK_DATES(1,2).start_date,
HCLT_GET_TASK_DATES(1,2).end_date
FROM DUAL;
db<>fiddle here
如果您能够更改此设计,那么最好在普通连接和聚合中进行(或者可能在低基数输入的情况下使用 left join lateral
)。
但是在 11g 及更高版本中,有一种方法可以使用 SQL 实现所需的结果,使用 dbms_xmlgen
包的能力来处理任意游标。下面是代码:
create table t_lkp (id,dt)
as
select
trunc(level/4 + 1)
, date '2022-01-01' + level
from dual
connect by level < 11
create or replace function f_lkp (
p_id in int
)
return sys_refcursor
as
o_res sys_refcursor;
begin
open o_res for
select
min(dt) as dtfrom
, max(dt) as dtto
from t_lkp
where id = p_id;
return o_res;
end;
/
with a as (
select
level as i,
dbms_xmlgen.getxmltype(
/*ctx doesn't accept sys_refcursor, so we had to create a context*/
ctx => DBMS_XMLGEN.NEWCONTEXT(f_lkp(level))
) as val
from dual
connect by level < 6
)
select
i
, xmlquery(
'/ROWSET/ROW/DTFROM/text()'
passing a.val returning content null on empty
) as dtfrom
, xmlquery(
'/ROWSET/ROW/DTTO/text()'
passing a.val returning content null on empty
) as dtto
from a
I | DTFROM | DTTO
-: | :------------------ | :------------------
1 | 2022-01-02 00:00:00 | 2022-01-04 00:00:00
2 | 2022-01-05 00:00:00 | 2022-01-08 00:00:00
3 | 2022-01-09 00:00:00 | 2022-01-11 00:00:00
4 | null | null
5 | null | null
db<>fiddle here
请注意,在大输入数据集和并行处理的情况下,它会打开过多的游标,这将极大地消耗资源。所以用plain join会好很多。
Function HCLT_GET_TASK_DATES(i_ownerid IN NUMBER, i_itemid IN NUMBER)
RETURN SYS_REFCURSOR IS
o_DATACUR SYS_REFCURSOR;
begin
open o_DATACUR for
select nvl(TO_CHAR(min(pref_start), 'DD-MON-YYYY'), '') AS MIN_DATE,
nvl(TO_CHAR(max(pref_finish), 'DD-MON-YYYY'), '') AS MAX_DATE
from autoplanallocation
WHERE project_id = i_ownerid
AND task_id = i_itemid;
RETURN o_DATACUR;
END;
/
SELECT HCLT_GET_TASK_DATES(267157, 15334208),
tv.taskid,
tv.wbs_code AS wbscode,
tv.taskcode,
tv.act_name,
ltrim(regexp_replace(tv.stageactorlovs, '[^#]*#(\d+?),', ','), ',') as stageactorlovs,
tv.createdat,
tv.pushedtoTaskModule,
tv.OVERALLSTATUS AS overallstatus1,
tv.ACTIVITY_CODE_ID,
tv.wbs_code,
TO_CHAR(tv.pref_st, 'DD-MON-YYYY') AS pref_st,
TO_CHAR(tv.pref_fn, 'DD-MON-YYYY') AS pref_fn,
tv.ACTL_EFFORT,
tv.rollup_effort,
tv.overAllStatus,
tv.FIELD5,
tv.FIELD4,
tv.activity_code_id
FROM task_view tv, autoplanallocation al
WHERE al.project_id = tv.ownerid(+)
and al.task_id = tv.taskid(+)
and tv.ownertype = 'Prj'
AND tv.ownerid = 267157
AND (tv.overAllStatus = 'All' OR 'All' = 'All')
AND (TaskId IN
((SELECT xyz
FROM (SELECT ToItemID xyz
FROM ItemTraceability it
WHERE it.FromOwnerType = 'Prj'
AND it.FromOwnerID = 267157
AND it.FromItemType = it.FromItemType
AND it.FromChildItemType = 'USTRY'
AND it.FromItemID = 15334208
AND it.ToOwnerType = 'Prj'
AND it.ToOwnerID = 267157
AND it.ToItemType = it.ToItemType
AND it.ToChildItemType = 'Tsk'
UNION ALL
SELECT FromItemID
FROM ItemTraceability it
WHERE it.ToOwnerType = 'Prj'
AND it.ToOwnerID = 267157
AND it.ToItemType = it.ToItemType
AND it.ToChildItemType = 'USTRY'
AND it.ToItemID = 15334208
AND it.FromOwnerType = 'Prj'
AND it.FromOwnerID = 267157
AND it.FromItemType = it.FromItemType
AND it.FromChildItemType = 'Tsk'))))
ORDER BY UPPER(wbs_code) ASC;
我认为没有使用 SQL 或 PL/SQL 代码解析嵌套游标的本机方法。
在 Java 中使用 Oracle JDBC 数据库驱动程序,您可以:
- 使用
oracle.jdbc.driver.OraclePreparedStatement.executeQuery
得到一个java.sql.ResultSet
- 可以转换为
oracle.jdbc.driver.OracleResultSet
- 然后您可以遍历结果集的行,对于每一行,您可以使用
oracle.jdbc.driver.OracleResultSet.getCursor()
来获取嵌套游标。- 然后,您可以使用与迭代外部游标完全相同的方式来迭代该嵌套游标,以从嵌套游标中提取行。
- 然后您应该关闭嵌套游标(尽管它会在包含父游标关闭时自动关闭)。
- 最后,关闭父游标。
如果您想要 SQL 解决方案,则不要 return 游标和 return 嵌套 table 集合数据类型。
或者,对于具有多列的单行,return一个对象类型:
CREATE TYPE date_range_obj AS OBJECT(
start_date DATE,
end_date DATE
)
/
CREATE FUNCTION HCLT_GET_TASK_DATES(
i_ownerid IN autoplanallocation.project_id%TYPE,
i_itemid IN autoplanallocation.task_id%TYPE
)
RETURN date_range_obj
IS
v_range date_range_obj;
begin
SELECT date_range_obj(MIN(pref_start), MAX(pref_finish))
INTO v_range
FROM autoplanallocation
WHERE project_id = i_ownerid
AND task_id = i_itemid;
RETURN v_range;
END;
/
然后,例如:
SELECT HCLT_GET_TASK_DATES(1,2).start_date,
HCLT_GET_TASK_DATES(1,2).end_date
FROM DUAL;
db<>fiddle here
如果您能够更改此设计,那么最好在普通连接和聚合中进行(或者可能在低基数输入的情况下使用 left join lateral
)。
但是在 11g 及更高版本中,有一种方法可以使用 SQL 实现所需的结果,使用 dbms_xmlgen
包的能力来处理任意游标。下面是代码:
create table t_lkp (id,dt) as select trunc(level/4 + 1) , date '2022-01-01' + level from dual connect by level < 11
create or replace function f_lkp ( p_id in int ) return sys_refcursor as o_res sys_refcursor; begin open o_res for select min(dt) as dtfrom , max(dt) as dtto from t_lkp where id = p_id; return o_res; end; /
with a as ( select level as i, dbms_xmlgen.getxmltype( /*ctx doesn't accept sys_refcursor, so we had to create a context*/ ctx => DBMS_XMLGEN.NEWCONTEXT(f_lkp(level)) ) as val from dual connect by level < 6 ) select i , xmlquery( '/ROWSET/ROW/DTFROM/text()' passing a.val returning content null on empty ) as dtfrom , xmlquery( '/ROWSET/ROW/DTTO/text()' passing a.val returning content null on empty ) as dtto from a
I | DTFROM | DTTO -: | :------------------ | :------------------ 1 | 2022-01-02 00:00:00 | 2022-01-04 00:00:00 2 | 2022-01-05 00:00:00 | 2022-01-08 00:00:00 3 | 2022-01-09 00:00:00 | 2022-01-11 00:00:00 4 | null | null 5 | null | null
db<>fiddle here
请注意,在大输入数据集和并行处理的情况下,它会打开过多的游标,这将极大地消耗资源。所以用plain join会好很多。