从Oracle存储过程获取结果集

50

我正在将一个存储过程从SQL Server转换到Oracle。这个存储过程提供了一个直接的结果集。我的意思是,如果你在例如Management Studio中调用存储过程,你会直接获得结果集。

在转换到Oracle时,我遇到了一个问题,即在Oracle中无法显示结果集。

我在互联网上搜索并发现,存储过程应该产生一个REF CURSOR,但我仍然遇到了一个问题,即如何编写一小段代码来获取结果集并处理它。

伪代码:

调用存储过程并获取游标 对游标执行某些操作,使我的结果集出现

有人有想法吗?


我想知道。这个问题已经有超过90K的浏览量,但只有20个赞。它应该按照浏览量来计算赞数。 :D - Dr. MAF
@Dr.MAF 这个问题现在已经有将近110,000次的浏览量了。如果你问我,这相当惊人。 - User1974
@Wilson 抱歉,我没有理解你的想法。我应该问你什么? - Dr. MAF
7个回答

74

在 SQL Plus 中:

SQL> create procedure myproc (prc out sys_refcursor)
  2  is
  3  begin
  4     open prc for select * from emp;
  5  end;
  6  /

Procedure created.

SQL> var rc refcursor
SQL> execute myproc(:rc)

PL/SQL procedure successfully completed.

SQL> print rc

     EMPNO ENAME      JOB              MGR HIREDATE           SAL       COMM     DEPTNO
---------- ---------- --------- ---------- ----------- ---------- ---------- ----------
      7839 KING       PRESIDENT            17-NOV-1981       4999                    10
      7698 BLAKE      MANAGER         7839 01-MAY-1981       2849                    30
      7782 CLARKE     MANAGER         7839 09-JUN-1981       2449                    10
      7566 JONES      MANAGER         7839 02-APR-1981       2974                    20
      7788 SCOTT      ANALYST         7566 09-DEC-1982       2999                    20
      7902 FORD       ANALYST         7566 03-DEC-1981       2999                    20
      7369 SMITHY     CLERK           7902 17-DEC-1980       9988         11         20
      7499 ALLEN      SALESMAN        7698 20-FEB-1981       1599       3009         30
      7521 WARDS      SALESMAN        7698 22-FEB-1981       1249        551         30
      7654 MARTIN     SALESMAN        7698 28-SEP-1981       1249       1400         30
      7844 TURNER     SALESMAN        7698 08-SEP-1981       1499          0         30
      7876 ADAMS      CLERK           7788 12-JAN-1983       1099                    20
      7900 JAMES      CLERK           7698 03-DEC-1981        949                    30
      7934 MILLER     CLERK           7782 23-JAN-1982       1299                    10
      6668 Umberto    CLERK           7566 11-JUN-2009      19999          0         10
      9567 ALLBRIGHT  ANALYST         7788 02-JUN-2009      76999         24         10

2
太好了!感谢你的回答,托尼。我能通过Unix/Linux脚本将这些结果导出为CSV吗? - Crash OR
11
在SQL Plus中,“print rc is nice”很不错,我该如何在SQL Developer中将rc显示为网格形式? - Stack0verflow
1
我很惊讶。这个问题已经有超过90K的浏览量,但只有20个赞。它应该是每个浏览量都应该点赞的。而你的回答应该是每个浏览量都应该得到10个赞。非常感谢。 - Dr. MAF
1
我想问与@Stack0verflow上面相同的问题。如何以网格格式显示此结果? - Muhammad Asim
1
我的救星!我已经卡在一个类似的问题上6个小时了。 - Isaiah
显示剩余2条评论

5
我的解决方案是创建一个流水线函数。它的优势在于查询可以写成一行代码:
  • select * from table(yourfunction(param1, param2));
  • 您可以将结果与其他表连接,或根据需要进行过滤或排序。
  • 查询结果看起来像常规查询结果,因此您可以轻松地操作它们。
要定义该函数,您需要执行以下操作:
  -- Declare the record columns
  TYPE your_record IS RECORD(
     my_col1 VARCHAR2(50), 
     my_col2 varchar2(4000)
  );
  TYPE your_results IS TABLE OF your_record;

  -- Declare the function
  function yourfunction(a_Param1 varchar2, a_Param2 varchar2)
  return your_results pipelined is
    rt          your_results;
  begin
    -- Your query to load the table type
    select s.col1,s.col2
    bulk collect into rt
    from your_table s
    where lower(s.col1) like lower('%'||a_Param1||'%');

    -- Stuff the results into the pipeline..
    if rt.count > 0 then 
      for i in rt.FIRST .. rt.LAST loop 
        pipe row (rt(i)); 
      end loop; 
    end if;

    -- Add more results as you please....
    return;
  end find;

如上所述,查看结果的所有步骤只需执行以下操作:

select * from table(yourfunction(param1, param2)) t order by t.my_col1;

2
这种解决方案的好处是结果就像常规选择语句一样返回,这意味着调用代码在填充某种数据结构(如表格或数组)时不需要做任何不同的事情(这是99%的情况下所需做的,以便在调用代码中进行进一步处理)。使用已接受的解决方案,结果会被打印出来,因此调用代码需要解析打印出的结果才能将数据转换为某种数据结构,这可能非常容易出现错误。 - Yabasa

4

Oracle不是SQL Server。在SQL Developer中尝试以下操作:

variable rc refcursor;
exec testproc(:rc2);
print rc2

3

您好,我知道这个问题已经问了一段时间,但我刚刚找到了解决方法,希望可以帮助其他人。不确定这是否完全符合您的要求,但这是我如何调用存储过程并使用SQL Developer查看输出的方法。
在SQL Developer中查看存储过程时,右键单击并选择“运行”或选择Ctrl + F11以打开Run PL / SQL窗口。 这将创建一个模板,其中包含输入和输出参数,需要进行修改。 我的存储过程返回sys_refcursor。 对我来说棘手的部分是声明一个行类型,它与由存储过程返回的select语句/sys_refcursor完全相同:

DECLARE
  P_CAE_SEC_ID_N NUMBER;
  P_FM_SEC_CODE_C VARCHAR2(200);
  P_PAGE_INDEX NUMBER;
  P_PAGE_SIZE NUMBER;
  v_Return sys_refcursor;
  type t_row is record (CAE_SEC_ID NUMBER,FM_SEC_CODE VARCHAR2(7),rownum number, v_total_count number);
  v_rec t_row;

BEGIN
  P_CAE_SEC_ID_N := NULL;
  P_FM_SEC_CODE_C := NULL;
  P_PAGE_INDEX := 0;
  P_PAGE_SIZE := 25;

  CAE_FOF_SECURITY_PKG.GET_LIST_FOF_SECURITY(
    P_CAE_SEC_ID_N => P_CAE_SEC_ID_N,
    P_FM_SEC_CODE_C => P_FM_SEC_CODE_C,
    P_PAGE_INDEX => P_PAGE_INDEX,
    P_PAGE_SIZE => P_PAGE_SIZE,
    P_FOF_SEC_REFCUR => v_Return
  );
  -- Modify the code to output the variable
  -- DBMS_OUTPUT.PUT_LINE('P_FOF_SEC_REFCUR = ');
  loop
    fetch v_Return into v_rec;
    exit when v_Return%notfound;
    DBMS_OUTPUT.PUT_LINE('sec_id = ' || v_rec.CAE_SEC_ID || 'sec code = ' ||v_rec.FM_SEC_CODE);
  end loop;

END;

3
FYI,自Oracle 12c起,您可以这样做:

CREATE OR REPLACE PROCEDURE testproc(n number)
AS
  cur SYS_REFCURSOR;
BEGIN
    OPEN cur FOR SELECT object_id,object_name from all_objects where rownum < n;
    DBMS_SQL.RETURN_RESULT(cur);
END;
/

EXEC testproc(3);

OBJECT_ID OBJECT_NAME                                                                                                                     
---------- ------------
100 ORA$BASE                                                                                                                        
116 DUAL                                                                                                                            

这个功能旨在让它更接近其他数据库,并简化迁移。但是对我来说并不完美,例如SQL开发人员无法像正常SELECT一样很好地显示它。
我更喜欢管道函数的输出,但它们需要更多的样板代码。
更多信息: https://oracle-base.com/articles/12c/implicit-statement-results-12cr1

1
@Ejrr1085 你使用了“运行脚本”(F5)吗?这对我有效。我不需要执行通常的“set serveroutput on”,但你也可以尝试一下。 - phil_w

2

在SQL Plus中:

SQL> var r refcursor
SQL> set autoprint on
SQL> exec :r := function_returning_refcursor();

将最后一行替换为对您的过程/函数的调用,refcursor的内容将被显示。


0
CREATE OR REPLACE PROCEDURE SP_Invoices(p_nameClient IN CHAR)
AS
BEGIN       
    FOR c_invoice IN 
    (
       SELECT CodeInvoice, NameClient FROM Invoice
       WHERE NameClient = p_nameClient
    )
    LOOP
        dbms_output.put_line('Code Invoice: ' || c_invoice.CodeInvoice);
        dbms_output.put_line('Name Client : ' ||  c_invoice.NameClient );
    END LOOP;
END;

在 SQL Developer 中执行:

BEGIN
    SP_Invoices('Perico de los palotes');
END;
-- Or:
EXEC SP_Invoices('Perico de los palotes');

输出:

> Code Invoice: 1 
> Name Client : Perico de los palotes
> Code Invoice: 2 
> Name Client : Perico de los palotes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接