PL/SQL中的refcursor的概念
Posted
技术标签:
【中文标题】PL/SQL中的refcursor的概念【英文标题】:Concept of refcursor in PL/SQL 【发布时间】:2014-11-15 04:39:08 【问题描述】:您好,只是想了解一下 refcursor。我创建了一个包如下 - 创建或替换包体 APPS.emp_pay_pkg 是
PROCEDURE emp_pay_period_proc(
p_user_id in varchar2,
p_pay_period_date out sys_refcursor,
p_success out varchar2)
IS
idx PLS_INTEGER := 0;
l_pay_start_period date;
l_pay_end_period date;
--Curso c_ap is fetching the pay period details.
BEGIN
idx := 0;
arempty (1) := '';
OPEN p_pay_period_date for
SELECT PTP.START_DATE PAY_PERIOD_START_DATE,
PTP.END_DATE PAY_PERIOD_END_DATE
FROM Pay_period ptp
where tso_td=p_user_id
GROUP BY PTP.START_DATE,PTP.END_DATE;
FETCH p_pay_period_date
INTO l_pay_start_period,l_pay_end_period;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line (SQLERRM);
p_success := 'Records get error out in Array ';
END;
END emp_pay_pkg;
/
now to display the data for testing i used -
DECLARE
l_user_id VARCHAR2 (200) := 'User_id'
l_pay_period_date sys_refcursor;
L_START_DATE DATE; L_END_DATE DATE;
l_success VARCHAR2 (200);
BEGIN
apps.emp_pay_period_pkg.emp_pay_period_proc (l_user_id, l_pay_period_date, l_success);
DBMS_OUTPUT.put_line ('l_pay_period_end_date'||'|'||'l_pay_period_start_date');
LOOP
FETCH l_pay_period_date
INTO L_START_DATE,L_END_DATE;
EXIT WHEN l_pay_period_date%NOTFOUND;
DBMS_OUTPUT.put_line (L_END_DATE||'|'||L_START_DATE);
END LOOP;
CLOSE l_pay_period_date;
DBMS_OUTPUT.put_line ('' || l_success);
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line ('Error :' || SQLERRM);
END;
奇怪的部分是我在另一个使用 ref 光标的包上使用了类似的概念,但我的一位同事告诉我,如果你已经获取了 curdor 并将其移动到包体中的变量中,那么光标会变为空,所以它不应该被循环并移动到匿名块中的变量中。
我认为这里的某个地方也应该适用于上述软件包。谁能澄清我的概念?
【问题讨论】:
【参考方案1】:我不太确定我是否完全理解您的要求。
游标本质上是一个指向可以执行以返回结果的程序的指针。它是一个只进的结构——你只能获取接下来的 n 行,你不能获取之前的 n 行(除非你关闭并重新打开游标如果基础数据发生变化,结果集可能会有所不同)。
因为游标是只进的,所以在打开它的过程和该过程的调用者中从游标中获取数据是非常不可能的。从语法上讲,这样做是完全有效的。但这意味着调用者永远不会看到查询返回的第一行,这极不可能是您想要的。如果你想从调用者的游标中获取数据,你的过程应该只打开游标。如果要从过程中的游标中获取数据,则不应将其返回给调用者。
顺便说一句,几乎可以肯定任何过程都没有“返回状态”参数。如果过程遇到无法解决的错误,它应该抛出异常。调用者不应该依赖于检查返回状态,它应该捕获它可以处理的特定异常,并让其余异常传播到调用堆栈。不执行 RAISE
的 WHEN others
异常处理程序(例如您在此处拥有的异常处理程序)几乎可以肯定是错误,其唯一影响是隐藏错误并删除有关错误发生位置的非常有用的信息。
【讨论】:
【参考方案2】:你的同事是对的。您的包过程不需要 FETCH 语句。它不需要局部变量。它不需要 WHEN OTHERS 异常处理程序。
如果你想返回一个引用游标,程序应该只打开游标。 例如,这就足够了:
PROCEDURE emp_pay_period_proc(p_user_id IN VARCHAR2, p_pay_period OUT SYS_REFCURSOR)
IS
BEGIN
OPEN p_pay_period FOR
SELECT ptp.start_date AS pay_period_start_date
, ptp.end_date AS pay_period_end_date
FROM pay_period ptp
WHERE ptp.tso_td = p_user_id
GROUP BY ptp.start_date, ptp.end_date;
END;
如果我需要返回一个引用游标,我个人的偏好是使用函数而不是过程。但是鉴于问题中提供的示例,我认为引用光标并不是最合适的设计。
【讨论】:
以上是关于PL/SQL中的refcursor的概念的主要内容,如果未能解决你的问题,请参考以下文章
Oracle - pl sql 从 SYS_REFCURSOR 中选择
将常规 CURSOR 传递给需要 SYS_REFCURSOR 的 PL/SQL 过程
使用 sys_refcursor 执行 pl sql 存储过程时出错
如何使用 Toad 工具显示 sys_refcursor 输出 pl sql