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 行(除非你关闭并重新打开游标如果基础数据发生变化,结果集可能会有所不同)。

因为游标是只进的,所以在打开它的过程和该过程的调用者中从游标中获取数据是非常不可能的。从语法上讲,这样做是完全有效的。但这意味着调用者永远不会看到查询返回的第一行,这极不可能是您想要的。如果你想从调用者的游标中获取数据,你的过程应该只打开游标。如果要从过程中的游标中获取数据,则不应将其返回给调用者。

顺便说一句,几乎可以肯定任何过程都没有“返回状态”参数。如果过程遇到无法解决的错误,它应该抛出异常。调用者不应该依赖于检查返回状态,它应该捕获它可以处理的特定异常,并让其余异常传播到调用堆栈。不执行 RAISEWHEN 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

Oracle 修改 sys refcursor 并在 PL/SQL 中返回修改后的游标

循环oracle pl/sql中的表名