函数返回 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 数据库驱动程序,您可以:


如果您想要 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会好很多。