21

In MS SQL Server if I want to check the results from a Stored procedure I might execute the following in Management Studio.

--SQL SERVER WAY
exec sp_GetQuestions('OMG Ponies')

The output in the results pane might look like this.

ID    Title                                             ViewCount   Votes 
----- ------------------------------------------------- ---------- --------
2165  Indexed View vs Indexes on Table                  491         2  
5068  SQL Server equivalent to Oracle’s NULLS FIRST     524         3 
1261  Benefits Of Using SQL Ordinal Position Notation?  377         2 

(3 row(s) affected)

No need to write loops or PRINT statements.

To do the same thing in Oracle I might execute the following anonymous block in SQL Developer

--ORACLE WAY
    DECLARE
        OUTPUT  MYPACKAGE.refcur_question;
        R_OUTPUT MYPACKAGE.r_question;
        USER    VARCHAR2(20);

BEGIN

  dbms_output.enable(10000000);
  USER:= 'OMG Ponies';
  recordCount := 0;



  MYPACKAGE.GETQUESTIONS(p_OUTPUT => OUTPUT, 
  p_USER=> USER, 

  ) ;




  DBMS_OUTPUT.PUT_LINE('ID |  Title | ViewCount | Votes' );

  LOOP 
    FETCH OUTPUT
    INTO R_OUTPUT;

         DBMS_OUTPUT.PUT_LINE(R_OUTPUT.QUESTIONID || '|' || R_OUTPUT.TITLE 
               '|' || R_OUTPUT.VIEWCOUNT '|' || R_OUTPUT.VOTES);
          recordCount := recordCount+1;




 EXIT WHEN OUTPUT % NOTFOUND;  
      END LOOP;
      DBMS_OUTPUT.PUT_LINE('Record Count:'||recordCount);
      CLOSE OUTPUT;


    END;

This outputs like

ID|Title|ViewCount|Votes 
2165|Indexed View vs Indexes on Table|491|2  
5068|SQL Server equivalent to Oracle’s NULLS FIRST|524|3 
1261|Benefits Of Using SQL Ordinal Position Notation?|377|2 
Record Count: 3

So the SQL version has 1 line and the oracle has 18 and the output is ugly. Its exacerbated if there are a lot of columns and/or the data is numeric.

What's odd to me about this is that if I write this statement in either SQL Developer or Management studio...

SELECT 
ID, 
Title, 
ViewCount, 
Votes
FROM votes where user = 'OMG Ponies'  

The results are fairly similar. This makes me feel like I'm either missing a technique or using the wrong tool.

Conrad Frix
  • 51,984
  • 12
  • 96
  • 155

3 Answers3

17

If GetQuestions is a function returning a refcursor, which seems to be what you have in the SQL Server version, then rather you may be able to do something like this:

select * from table(MyPackage.GetQuestions('OMG Ponies'));

Or if you need it in a PL/SQL block then you can use the same select in a cursor.

You can also have the function produce the dbms_output statements instead so they're always available for debugging, although that adds a little overhead.

Edit

Hmmm, not sure it's possible to cast() the returned refcursor to a usable type, unless you're willing to declare your own type (and a table of that type) outside the package. You can do this though, just to dump the results:

create package mypackage as
    function getquestions(user in varchar2) return sys_refcursor;
end mypackage;
/

create package body mypackage as
    function getquestions(user in varchar2) return sys_refcursor as
        r sys_refcursor;
    begin
        open r for
            /* Whatever your real query is */
            select 'Row 1' col1, 'Value 1' col2 from dual
            union
            select 'Row 2', 'Value 2' from dual
            union
            select 'Row 3', 'Value 3' from dual;
            return r;
    end;
end mypackage;
/

var r refcursor;
exec :r := mypackage.getquestions('OMG Ponies');
print r;

And you can use the result of the call in another procedure or function; it's just getting to it outside PL/SQL that seems to be a little tricky.

Edited to add: With this approach, if it's a procedure you can do essentially the same thing:

var r refcursor;
exec mypackage.getquestions(:r, 'OMG Ponies');
print r;
Alex Poole
  • 183,384
  • 11
  • 179
  • 318
  • The SQL Server version probably is not a function. In SQL Server, stored procedures that return set like results just have `select` statements that are not bound to a cursor. Getting the results of the execution of such a stored procedure is the same as getting the results from a `select` query on the client side. – Shannon Severance Aug 20 '10 at 03:57
  • I should learn about SQL Server one day. Seems like the equivalent would be a function that returns a refcursor though, rather than a procedure with an `OUT` parameter, so it can be used in a query from plan SQL? – Alex Poole Aug 20 '10 at 07:09
  • MyPackage.GetQuestions is a procedure. e.g. PROCEDURE GETQUETSIONS(output IN OUT MYPACKAGE.refcur_question, user IN VARCHAR2). When I tied your technique I get invalid identifier errors – Conrad Frix Aug 20 '10 at 17:31
  • I was thinking you could make it a function instead; `FUNCTION GETQUESTIONS(user IN VARCHAR2) RETURNS refcur_question`. But you may not be able to modify it; and from memory you might have to define the return type (`refcur_question`) at schema level, not inside the package, but it's been a while... – Alex Poole Aug 20 '10 at 17:52
  • 1
    If you are keeping the existing procedure, try `exec mypackage.getquestions(:r, 'OMG Ponies');` instead of having the extra function to do the conversion. I'd fixated on a function because I thought it would cast, but as it doesn't a procedure is fine (and possibly more 'standard'). So you still have 3 lines replacing your original 18 *8-) – Alex Poole Aug 21 '10 at 11:35
  • Alex that worked and what I was looking for but the function was interesting as well – Conrad Frix Aug 23 '10 at 14:58
  • If it's a function you don't even need a variable, just `select mypackage.getquestions('OMG Ponies') from dual;` – Jon Heller Feb 23 '11 at 19:59
3

SQL Developer automatically catches the output from running your stored procedures. Running the stored procedure directly from our procedure editor, you can see this behavior detailed in my post here

SQL Developer Tip: Viewing REFCURSOR Output

Now, if you want to run the refcursor as part of an anon block in our SQL Worksheet, you could do something similar to this

var rc refcursor
exec :rc := GET_EMPS(30)
print rc

--where GET_EMPS() would be your sp_GetQuestions('OMG Ponies') call. The PRINT command sends the output from the 'query' which is ran via the stored procedure, and looks like this:

anonymous block completed
RC
-----------------------------------------------------------------------------------------------------
EMPLOYEE_ID FIRST_NAME           LAST_NAME                 EMAIL                     PHONE_NUMBER         HIRE_DATE                 JOB_ID     SALARY     COMMISSION_PCT MANAGER_ID DEPARTMENT_ID 
----------- -------------------- ------------------------- ------------------------- -------------------- ------------------------- ---------- ---------- -------------- ---------- ------------- 
114         Den                  Raphaely                  DRAPHEAL                  515.127.4561         07-DEC-94 12.00.00        PU_MAN     11000                     100        30            
115         Alexander            Khoo                      AKHOO                     515.127.4562         18-MAY-95 12.00.00        PU_CLERK   3100                      114        30            
116         Shelli               Baida                     SBAIDA                    515.127.4563         24-DEC-97 12.00.00        PU_CLERK   2900                      114        30            
117         Sigal                Tobias                    STOBIAS                   515.127.4564         24-JUL-97 12.00.00        PU_CLERK   2800                      114        30            
118         Guy                  Himuro                    GHIMURO                   515.127.4565         15-NOV-98 12.00.00        PU_CLERK   2600                      114        30            
119         Karen                Colmenares                KCOLMENA                  515.127.4566         10-AUG-99 12.00.00        PU_CLERK   2500                      114        30            

Now, you said 10g. If you're in 12c, we have enhanced the PL/SQL engine to support implicit cursor results. So this gets a bit easier, no more setting up the cursor, you just make a call to get the data, as documented here: http://docs.oracle.com/database/121/DRDAA/migr_tools_feat.htm#DRDAA230

Conrad Frix
  • 51,984
  • 12
  • 96
  • 155
thatjeffsmith
  • 20,522
  • 6
  • 37
  • 120
  • I was actually looking at this feature yesterday. It's nice but kind of a PIA to get to in that I can't just open a "Run PL/SQL" window Thanks for the 12c tip. We're still on 11G so I look forward to using it eventually. Also I fixed up the formatting of your post. See [Editing-help](http://stackoverflow.com/editing-help) for info on how I did it – Conrad Frix Jul 23 '14 at 19:39
  • So you want a 'run PL/SQL' window that will auto-grab the output, like our Procedure Editor offers when executing a single unit? Not a bad idea...I'll kick it around the group and see where it goes. – thatjeffsmith Jul 24 '14 at 11:33
0
/*
    Create Sample Package in HR Schema
*/

CREATE OR REPLACE PACKAGE PRINT_REF_CURSOR
AS
    PROCEDURE SP_S_EMPLOYEES_BY_DEPT (
        p_DEPARTMENT_ID   IN  INTEGER,
        Out_Cur OUT SYS_REFCURSOR); 

END PRINT_REF_CURSOR;        

CREATE OR REPLACE PACKAGE BODY PRINT_REF_CURSOR
AS

    PROCEDURE SP_S_EMPLOYEES_BY_DEPT (
        p_DEPARTMENT_ID   IN  INTEGER,
        Out_Cur OUT SYS_REFCURSOR)
    AS 
    BEGIN
      OPEN Out_Cur FOR
           SELECT *
             FROM EMPLOYEES
             WHERE DEPARTMENT_ID = p_DEPARTMENT_ID;
    EXCEPTION
      WHEN NO_DATA_FOUND
      THEN
         DBMS_OUTPUT.Put_Line('SP_S_EMPLOYEES_BY_DEPT' || ',' || '-20000' || ',' );
      WHEN OTHERS
      THEN
         DBMS_OUTPUT.Put_Line('SP_S_EMPLOYEES_BY_DEPT' || ',' || '-20001' || ',' );    
    END SP_S_EMPLOYEES_BY_DEPT;         

END PRINT_REF_CURSOR;    

/*
    Fetch values using Ref Cursor and display it in grid.
*/

var RC refcursor;

DECLARE 
    p_DEPARTMENT_ID NUMBER;
    OUT_CUR SYS_REFCURSOR;

BEGIN 
  p_DEPARTMENT_ID := 90;
  OUT_CUR := NULL;

  PRINT_REF_CURSOR.SP_S_EMPLOYEES_BY_DEPT ( p_DEPARTMENT_ID, OUT_CUR);
  :RC := OUT_CUR;

END;
/
PRINT RC;  
/************************************************************************/