【问题标题】:trouble defining weakly defined ref cursor定义弱定义的参考游标的麻烦
【发布时间】:2025-12-25 08:15:11
【问题描述】:

我正在尝试编写一个存储过程,它接受一个数字 n,并返回给定查询的前 n 个结果,专门锁定这 n 行。我对 SQL 有点陌生,并且在正确匹配数据类型时遇到了一些困难。

我的包装规格如下所示:

PACKAGE package IS
   Type out_result_type is REF CURSOR;
   PROCEDURE stored_proc
     (in_n IN NUMBER DEFAULT 10, 
        out_list IN OUT out_result_type);

然后我在过程体中定义光标,如下所示:

CURSOR OUT_RESULT_TYPE IS
        SELECT a.id
            FROM schema.table a
            WHERE (some conditions) AND rownum <= in_n;

稍后我尝试将光标的结果提取到输出变量中:

OPEN OUT_RESULT_TYPE;
FETCH OUT_RESULT_TYPE INTO out_list; -- error on this line
CLOSE OUT_RESULT_TYPE;

但是很遗憾,这段代码无法编译; oracle 抱怨 out_list 已使用冲突的数据类型定义。知道如何解决这个问题吗?快把我逼疯了!

提前致谢。

【问题讨论】:

    标签: sql oracle


    【解决方案1】:
    CREATE OR REPLACE PACKAGE pkg_test
    AS
            TYPE    tt_cur IS REF CURSOR;
            PROCEDURE prc_cur (retval OUT tt_cur);
    END;
    
    CREATE OR REPLACE PACKAGE BODY pkg_test
    AS
            PROCEDURE prc_cur (retval OUT tt_cur)
            AS
            BEGIN
                    OPEN    retval
                    FOR
                    SELECT  *
                    FROM    dual;
            END;
    END;
    

    如果要锁定,请使用:

    CREATE OR REPLACE PACKAGE BODY pkg_test
    AS
            PROCEDURE prc_cur (retval OUT tt_cur)
            AS
            BEGIN
                    OPEN    retval
                    FOR
                    SELECT  a.id
                    FROM    schema.table a
                    WHERE   (some conditions)
                            AND rownum <= in_n
                    ORDER BY
                            column
                    -- Never forget ORDER BY!
                    FOR UPDATE;
            END;
    END;
    

    【讨论】:

    • @tuinstoel:我总是忘记它的存在 :)
    【解决方案2】:

    两个备注:

    1. 游标未锁定。
    2. 你不必做Type out_result_type is REF CURSOR;,使用默认类型sys_refcursor。见这里:Oracle - How to have an out ref cursor parameter in a stored procedure?

    【讨论】:

      【解决方案3】:

      您的out_list 必须是错误的类型。考虑(脚本在 10.2.0.3 上运行):

      CREATE TABLE t AS SELECT ROWNUM ID FROM all_objects WHERE ROWNUM <= 100;
      
      CREATE OR REPLACE PACKAGE cursor_pck AS
         TYPE out_result_type is REF CURSOR;
         PROCEDURE stored_proc (p_in IN NUMBER DEFAULT 10, 
                                p_out_list IN OUT out_result_type);
      END cursor_pck;
      /
      

      如果您想同时选择和锁定行,您可以使用FOR UPDATE 子句:

      CREATE OR REPLACE PACKAGE BODY cursor_pck AS
         PROCEDURE stored_proc (p_in IN NUMBER DEFAULT 10,  
                                p_out_list IN OUT out_result_type) IS
         BEGIN
            OPEN p_out_list FOR SELECT a.id FROM t a WHERE ROWNUM <= p_in FOR UPDATE;
         END stored_proc;
      END cursor_pck;
      /
      

      通过以下设置,您将像这样调用该过程:

      SQL> SET SERVEROUTPUT ON;
      SQL> DECLARE
        2     l_cursor cursor_pck.out_result_type;
        3     l_id t.id%TYPE;
        4  BEGIN
        5     cursor_pck.stored_proc(3, l_cursor);
        6     LOOP
        7        FETCH l_cursor INTO l_id;
        8        EXIT WHEN l_cursor%NOTFOUND;
        9        dbms_output.put_line(l_id);
       10     END LOOP;
       11  END;
       12  /
      
      1
      2
      3
      
      PL/SQL procedure successfully completed
      

      【讨论】:

        【解决方案4】:

        这不会像它写的那样工作,因为

        1. out_list 需要一个游标,而不是游标结果。
        2. 名称out_result_type 已用于类型,因此您不能将其重新定义为同一范围内的游标

        【讨论】:

          【解决方案5】:

          Oracle 提供了一个预定义的弱引用游标:sys_refcursor。在使用中它看起来像:

          CREATE OR REPLACE PACKAGE pkg_test
          AS
              PROCEDURE prc_cur (p_retval OUT sys_refcursor,
                                 p_lookup IN  VARCHAR2);
          END pkg_test;
          
          
          CREATE OR REPLACE PACKAGE BODY pkg_test 
          AS
              PROCEDURE prc_cur(p_retval OUT sys_refcursor
                                p_lookup IN  VARCHAR2) 
              IS
              BEGIN
                  OPEN retval FOR SELECT a.value 
                                    FROM tblname a 
                                   WHERE a.id <= p_lookup;
              END prc_cur;
          END pkg_test;
          

          这省去了需要声明类型的麻烦。 sys_refcursor 是指向打开游标的结果集的指针。如果您熟悉 Java,它与 java.sql.ResultSet 对象的概念相同,后者提供了一种获取查询结果的方法。

          【讨论】: