ref cursor examples

21
Consider the following schema :- create or replace procedure a is begin null; -- a depends on nothing end; / create or replace procedure b is begin a; -- b depends on a end; / create or replace procedure c is begin b; -- c depends on b end; / create or replace procedure d is begin c; -- d depends on c end; / create or replace procedure e is begin a; -- e depends on a d; -- and d end; / This is my artists impression :- a 1

Upload: jeeendra

Post on 19-Nov-2014

136 views

Category:

Documents


1 download

TRANSCRIPT

Page 1: Ref Cursor Examples

Consider the following schema :-

create or replaceprocedure a isbegin null; -- a depends on nothingend;/

create or replaceprocedure b isbegin a; -- b depends on aend;/

create or replaceprocedure c isbegin b; -- c depends on bend;/

create or replaceprocedure d isbegin c; -- d depends on cend;/

create or replaceprocedure e isbegin a; -- e depends on a d; -- and dend;/

This is my artists impression :-

a |\ | b | | | c | | | d |/ e

1

Page 2: Ref Cursor Examples

We can tell visually that the correct drop order would be 'e', 'd', 'c', 'b', 'a' - this is the longest path.

The only object that can be dropped is 'e', because no other objects depend on it (ie it is not referenced).

Refferring to step 3, we then select back child rows. ie rows which 'e' depend on. We can see clearly that it will pull back 'a' and 'd'. But 'b' references 'a', so we can not drop it yet.

To do this properly, we would need some mechanism to exclude 'a', until all the objects that depend on it have been processed, but currently we can not use a sub-query in the connect-by clause.

So I'm afraid that (in my fallible opinion) it can not be done with one or two queries.

When you use native dynamic sql, most of the times you use executeimmediate statment and to this statement you pass your sql code which isthen executed.When you use DBMS_SQL - you use a package that enables you to pass sqlcode to Oracle engine. In this package you have procedure for openingcurrsofr, fetching and so on...Native Dynamic SLQ is easier to use because is integrated with sql, toexecute statment in PL/SQL you just write EXECUTE IMMEDIATE stmt, withDBMS_SQL you have to use several procedures for exactly the same sql code.Native is also faster than DBMS_SQL because you avoid overhead for callinganother packages (like in DBMS_SQL).DBMS_SQL has also some advanteges over Nativefor example you have functionality similar to describe command in SQL*PlusDBMS_SQL supports code larger than 32K, native don't !!!DBMS_SQL supports RETURNING clause for multirow statments native only forsingle rows(i think about update or delete DML statment)

Using Ref Cursors To Return Recordsets

Since Oracle 7.3 REF CURSORS have been available which allow recordsets to be returned from stored procedures, functions and packages. The example below uses a ref cursor to return a subset of the records in the EMP table.

First, a package definition is needed to hold the ref cursor type:CREATE OR REPLACE PACKAGE Types AS TYPE cursor_type IS REF CURSOR;END Types; /Note. In Oracle9i the SYS_REFCURSOR type has been added making this first step unnecessary. If you are using Oracle9i or later simply ignore this first package and replace any references to Types.cursor_type with SYS_REFCURSOR.

Next a procedure is defined to use the ref cursor:

2

Page 3: Ref Cursor Examples

CREATE OR REPLACEPROCEDURE GetEmpRS (p_deptno IN emp.deptno%TYPE, p_recordset OUT Types.cursor_type) AS BEGIN OPEN p_recordset FOR SELECT ename, empno, deptno FROM emp WHERE deptno = p_deptno ORDER BY ename;END GetEmpRS;/The resulting cursor can be referenced from PL/SQL as follows:SET SERVEROUTPUT ON SIZE 1000000DECLARE v_cursor Types.cursor_type; v_ename emp.ename%TYPE; v_empno emp.empno%TYPE; v_deptno emp.deptno%TYPE;BEGIN GetEmpRS (p_deptno => 30, p_recordset => v_cursor); LOOP FETCH v_cursor INTO v_ename, v_empno, v_deptno; EXIT WHEN v_cursor%NOTFOUND; DBMS_OUTPUT.PUT_LINE(v_ename || ' | ' || v_empno || ' | ' || v_deptno); END LOOP; CLOSE v_cursor;END;/

declare 2 p_deptno number default 10; 3 begin 4 open :x for 5 'select deptno, dname, cursor( select ename from emp where emp.deptno = dept.deptno ) 6 from dept 7 where deptno = :p_deptno' USING p_deptno; 8 end; 9 /

PL/SQL procedure successfully completed.

3

Page 4: Ref Cursor Examples

Oracle ref cursors allow you to define queries as stored procedures in the Database. They can be used in java by executing a call through a callable statement to the stored procedure and casting the returned ref. cursors to ResultSet. Using the Reference cursors is a bit tricky. The following is the approach that allows using the Reference cursors in Java Database programming. Here is an example of a Stored Procedure using Ref Cursor. /* * Package Specification */ CREATE OR REPLACE PACKAGE REF_CURSOR_PROC AS // Declare reference cursor TYPE CURSOR_TYPE IS REF CURSOR; // register all the IN and OUT parameters, // Note: To avoid the sensitivity of java drivers declare the OUT parameters // after the IN parameters PROCEDURE getMenu ( PRODUCT_TYPE IN VARCHAR2, PRODUCT_CATEGORY IN VARCHAR2, PRICE IN VARCHAR2, RESULTS_MENU OUT CURSOR_TYPE ); End REF_CURSOR_PROC; / Ravi Kumar B Page 4 1/20/2005 A white paper on Java Database Interaction Mechanism /* * Declare Package Body */ CREATE OR REPLACE PACKAGE BODY REF_CURSOR_PROC AS /* * Sequence of parameter declaration and signature must be same as * Package specification. */ PROCEDURE getSubMenu ( PRODUCT_TYPE IN VARCHAR2, PRODUCT_CATEGORY IN VARCHAR2, PRICE IN VARCHAR2, RESULTS_SUB_MENU OUT CURSOR_TYPE ) AS BEGIN /* * Opening the reference cursor is very simple by stating a simple open * Statement followed by FOR to hold the out put of the SQL query. */ OPEN RESULTS_SUB_MENU FOR SELECT PMD.fkmenuid, MM.menuname, MM.cid, MM.SID, MM.hotflag, PMD.amount, PMD.parentmenuid FROM packagemenudetails PMD, menumaster MM

4

Page 5: Ref Cursor Examples

WHERE PMD.fkmenuid = MM.menuid AND (MM.expiry_date IS NULL OR MM.expiry_date > SYSDATE) AND PMD.parentmenuid = menu_id AND PMD.fkpackageid = package_id AND UPPER (PMD.fklanguageid) LIKE language_id AND UPPER (MM.fkhandsetid) LIKE handset_id ORDER BY PMD.menuitemsorder; END getSubMenu; END

Example Stored Procedure

Collapse Copy CodeCREATE OR REPLACEPROCEDURE GetEmpRS1 (p_recordset1 OUT SYS_REFCURSOR, p_recordset2 OUT SYS_REFCURSOR, PARAM IN STRING) ASBEGIN OPEN p_recordset1 FOR SELECT RET1 FROM MYTABLE WHERE LOOKUPVALUE > PARAM;

OPEN p_recordset2 FOR SELECT RET2 FROM MYTABLE WHERE LOOKUPVALUE >= PARAM;END GetEmpRS1;This stored procedure takes an input parameter for lookup, and has two OUT REF CURSORS. For simplicity of this example, both the REF CURSORS return a column (a single column). That is not a requirement in real life of course, you can as well associate a REF CURSOR with a SELECT statement like: SELECT * ....

What is a REF CURSOR

Cursors, as you know, help return recordsets/ resultsets. There may be another more technically correct definition of a cursor, but with my limited knowledge of databases, that statement sounds correct. A SQL Server stored procedure can return "a resultset" with a simple SELECT statement. It can even return multiple recordsets using multiple SELECT statements. Can Oracle do that? Single recordset, of course. Multiple recordsets - you need what is called a REF CURSOR. Treated just like a data type, your stored procedure takes REF CURSORS as OUT parameters, and you can return a full recordset in each REF CURSOR parameter back to the caller. So you can include as many REF CURSOR parameters as you want - your stored procedure will have the ability to return that many recordsets. Cool, huh?

5

Page 6: Ref Cursor Examples

Oracle/PLSQL: Procedure that outputs a dynamic PLSQL cursor

Question:  In Oracle, I have a table called "wine" and a stored procedure that outputs a cursor based on the "wine" table.I've created an HTML Form where the user can enter any combination of three values to retrieve results from the "wine" table. My problem is that I need a general "select" statement that will work no matter what value(s), the user enters.

Example:

parameter_1= "Chianti"parameter_2= "10"parameter_3= wasn't entered by the user but I have to use in the select statement. And this is my problem. How to initialize this parameter to get all rows for column3?SELECT * FROM wineWHERE column1 = parameter_1AND column2 = parameter_2AND column3 = parameter_3;.The output of my stored procedure must be a cursor.

Answer:  To solve your problem, you will need to output a dynamic PLSQL cursor in Oracle.Let's take a look at how we can do this. We've divided this process into 3 steps.

Step 1 - Table Definition

First, we need a table created in Oracle called "wine". Below is the create statement for the wine table.create table wine( col1 varchar2(40),  col2 varchar2(40),  col3 varchar2(40));We've made this table definition very simple, for demonstration purposes.

Step 2 - Create package

Next, we've created a package called "winepkg" that contains our cursor definition. This needs to be done so that we can use a cursor as an output parameter in our stored procedure.

create or replace PACKAGE winepkgIS   /* Define the REF CURSOR type. */

6

Page 7: Ref Cursor Examples

   TYPE wine_type IS REF CURSOR RETURN wine%ROWTYPE;END winepkg;This cursor will accept all fields from the "wine" table.

Step 3 - Create stored procedure

Our final step is to create a stored procedure to return the cursor. It accepts three parameters (entered by the user on the HTML Form) and returns a cursor (c1) of type "wine_type" which was declared in Step 2.The procedure will determine the appropriate cursor to return, based on the value(s) that have been entered by the user (input parameters).create or replace procedure find_wine2  (col1_in in varchar2,   col2_in in varchar2,   col3_in in varchar2,   c1 out winepkg.wine_type)asBEGIN   /* all columns were entered */   IF (length(col1_in) > 0) and (length(col2_in) > 0) and (length(col3_in) > 0)   THEN      OPEN c1 FOR      select *      from wine      where wine.col1 = col1_in      and  wine.col2 = col2_in      and  wine.col3 = col3_in;   /* col1 and col2 were entered */   ELSIF (length(col1_in) > 0) and (length(col2_in) > 0) and (length(col3_in) = 0)   THEN      OPEN c1 FOR      select *      from wine      where wine.col1 = col1_in      and  wine.col2 = col2_in;   /* col1 and col3 were entered */   ELSIF (length(col1_in) > 0) and (length(col2_in) = 0) and (length(col3_in) > 0)   THEN      OPEN c1 FOR      select *      from wine      where wine.col1 = col1_in      and  wine.col3 = col3_in;   /* col2 and col3 where entered */   ELSIF (length(col1_in) = 0) and (length(col2_in) > 0) and (length(col3_in) > 0)   THEN      OPEN c1 FOR      select *      from wine      where wine.col2 = col2_in      and  wine.col3 = col3_in;

7

Page 8: Ref Cursor Examples

   /* col1 was entered */   ELSIF (length(col1_in) > 0) and (length(col2_in) = 0) and (length(col3_in) = 0)   THEN      OPEN c1 FOR      select *      from wine      where wine.col1 = col1_in;   /* col2 was entered */   ELSIF (length(col1_in) = 0) and (length(col2_in) > 0) and (length(col3_in) = 0)   THEN      OPEN c1 FOR      select *      from wine      where wine.col2 = col2_in;   /* col3 was entered */   ELSIF (length(col1_in) = 0) and (length(col2_in) = 0) and (length(col3_in) > 0)   THEN      OPEN c1 FOR      select *      from wine      where wine.col3 = col3_in;   END IF;

END find_wine2;

Oracle 9i Environment

SQL> set serveroutput on SQL> CREATE TYPE OUT_TY AS OBJECT ( 2 COl1 VARCHAR2(1)) 3 /Type created.

SQL> CREATE TYPE OUT_TBL_TY AS TABLE OF OUT_TY; 2 /Type created.

SQL> CREATE OR REPLACE FUNCTION OUT_FN 2 RETURN OUT_TBL_TY PIPELINED IS 3 PRAGMA AUTONOMOUS_TRANSACTION; 4 TYPE ref0 IS REF CURSOR; 5 cur0 ref0; 6 out_rec out_ty := out_ty(NULL); 7 BEGIN 8 OPEN cur0 FOR 'select dummy from dual'; 9 LOOP 10 FETCH cur0 INTO out_rec.col1;

8

Page 9: Ref Cursor Examples

11 EXIT WHEN cur0%NOTFOUND; 12 dbms_output.put_line('Display from OUT_FN Line 1'); 13 PIPE ROW(out_rec); 14 dbms_output.put_line('Display from OUT_FN Line 2'); 15 END LOOP; 16 CLOSE cur0; 17 RETURN; 18 END OUT_FN; 19 /Function created.

SQL> SELECT a.col1 FROM TABLE(OUT_FN) a 2 /C-X

SQL> exec dbms_output.put_line('');Display from OUT_FN Line 1Display from OUT_FN Line 2

PL/SQL procedure successfully completed.

Oracle 10g Environment

SQL> set serveroutput onSQL> CREATE TYPE OUT_TY AS OBJECT ( 2 COl1 VARCHAR2(1)) 3 /Type created.

SQL> CREATE TYPE OUT_TBL_TY AS TABLE OF OUT_TY; 2 /Type created.

SQL> CREATE OR REPLACE FUNCTION OUT_FN 2 RETURN OUT_TBL_TY PIPELINED IS 3 PRAGMA AUTONOMOUS_TRANSACTION; 4 TYPE ref0 IS REF CURSOR; 5 cur0 ref0; 6 out_rec out_ty := out_ty(NULL); 7 BEGIN 8 OPEN cur0 FOR 'select dummy from dual'; 9 LOOP 10 FETCH cur0 INTO out_rec.col1;

9

Page 10: Ref Cursor Examples

11 EXIT WHEN cur0%NOTFOUND; 12 dbms_output.put_line('Display from OUT_FN Line 1'); 13 PIPE ROW(out_rec); 14 dbms_output.put_line('Display from OUT_FN Line 2'); 15 END LOOP; 16 CLOSE cur0; 17 RETURN; 18 END OUT_FN; 19 /Function created.

SQL> SELECT a.col1 FROM TABLE(OUT_FN) a 2 /C-X

Display from OUT_FN Line 1Display from OUT_FN Line 2

CREATE OR REPLACE PROCEDURE Ref_Cur1 (p_cons_ref OUT sys_Refcursor)AS BEGINOPEN p_cons_ref FOR SELECT cons_ref,sdo_cd,acc_no FROM eaudit.EA_CONSUMER_CLUS WHERE sdo_cd = '1110' ; END;/

---calling ref cursor from sql prompt and TOAD.

DECLARE P_CONS_REF sys_Refcursor; cons_ref VARCHAR2(10); sdo_cd VARCHAR2(4); acc_no VARCHAR2(4);BEGIN EAUDIT.Ref_Cur1(P_CONS_REF );LOOPFETCH P_CONS_REF INTO CONS_REF,SDO_CD,ACC_NO ;EXIT WHEN P_CONS_REF%NOTFOUND;DBMS_OUTPUT.PUT_LINE(CONS_REF||','||SDO_CD||','||ACC_NO);END LOOP;END;

10

Page 11: Ref Cursor Examples

With the REF_CURSOR you can return a recordset/cursor from a stored procedure.

There are 2 basic types: Strong ref cursor and weak ref cursorFor the strong ref cursor the returning columns with datatype and length need to be known at compile time.For the weak ref cursor the structure does not need to be known at compile time.

The STRONG_REF_CURSOR and until Oracle 9i also the weak-type need to be declared in a package structure lik this:create or replace package REFCURSOR_PKG as TYPE WEAK8i_REF_CURSOR IS REF CURSOR; TYPE STRONG_REF_CURSOR IS REF CURSOR RETURN EMP%ROWTYPE;end REFCURSOR_PKG;

The pl/sql procedure that returns a ref-cursor looks like this:

/** until Oracle 9 */create or replace procedure test( p_deptno IN number , p_cursor OUT REFCURSOR_PKG.WEAK8i_REF_CURSOR)isbegin open p_cursor FOR select * from emp where deptno = p_deptno;end test;

Since Oracle 9i you can use SYS_REFCURSOR as the type for the returning REF_CURSOR.

/** From Oracle 9 */create or replace procedure test( p_deptno IN number , p_cursor OUT SYS_REFCURSOR)isbegin open p_cursor FOR select * from emp where deptno = p_deptno;end test;

/* Strong type */

create or replace procedure test( p_deptno IN number , p_cursor OUT REFCURSOR_PKG.STRONG REF_CURSOR)

11

Page 12: Ref Cursor Examples

isbegin open p_cursor FOR select * from emp where deptno = p_deptno;end test;

Introduction to REF CURSOR

A REF CURSOR is basically a data type.  A variable created based on such a data type is generally called a cursor variable.  A cursor variable can be associated with different queries at run-time.  The primary advantage of using cursor variables is their capability to pass result sets between sub programs (like stored procedures, functions, packages etc.).

Let us start with a small sub-program as follows:

declare  type r_cursor is REF CURSOR;  c_emp r_cursor;  en emp.ename%type;begin  open c_emp for select ename from emp;  loop      fetch c_emp into en;      exit when c_emp%notfound;      dbms_output.put_line(en);  end loop;  close c_emp;end;

Let me explain step by step.  The following is the first statement you need to understand:

  type r_cursor is REF CURSOR;

The above statement simply defines a new data type called "r_cursor," which is of the type REF CURSOR.  We declare a cursor variable named "c_emp" based on the type "r_cursor" as follows:

  c_emp r_cursor;

Every cursor variable must be opened with an associated SELECT statement as follows:

  open c_emp for select ename from emp;

To retrieve each row of information from the cursor, I used a loop together with a FETCH statement as follows:

12

Page 13: Ref Cursor Examples

  loop      fetch c_emp into en;      exit when c_emp%notfound;      dbms_output.put_line(en);  end loop;

I finally closed the cursor using the following statement:

  close c_emp;

%ROWTYPE with REF CURSOR

In the previous section, I retrieved only one column (ename) of information using REF CURSOR.  Now I would like to retrieve more than one column (or entire row) of information using the same.  Let us consider the following example:

declare  type r_cursor is REF CURSOR;  c_emp r_cursor;  er emp%rowtype;begin  open c_emp for select * from emp;  loop      fetch c_emp into er;      exit when c_emp%notfound;      dbms_output.put_line(er.ename || ' - ' || er.sal);  end loop;  close c_emp;end;

In the above example, the only crucial declaration is the following:

  er emp%rowtype;

The above declares a variable named "er," which can hold an entire row from the "emp" table.  To retrieve the values (of each column) from that variable, we use the dot notation as follows:

      dbms_output.put_line(er.ename || ' - ' || er.sal);

ref cursor is a data structure which points to an object which in turn points to the memory location.

ex:

create or replace procedure test() as

13

Page 14: Ref Cursor Examples

begin

type ref_cursor is ref cursor;

open ref_cursor as

select * from table_name;

end;

There are 2 types in this.

1.strong ref cursor:

This has a return type defined.

2. weak ref cursor.

this doesnt have a return type

normal cursor:

Nothing but the named memory location.

it has 2 types

1. explicit cursor

Need to be defined whenever required.

2.Implicit cursor

need not defined and used by oracle implicitly in DML operation.

A REF CURSOR is basically a data type. A variable created based on such a data type is generally called a cursor variable. A cursor variable can be associated with different queries at run-time. The primary advantage of using cursor variables is their capability to pass result sets between sub programs (like stored procedures functions packages etc.).

Example :-

declaretype r_cursor is REF CURSOR;c_emp r_cursor;type rec_emp is record(name varchar2(20) sal number(6));er rec_emp;procedure PrintEmployeeDetails isbeginloopfetch c_emp into er;exit when c_emp notfound;

14

Page 15: Ref Cursor Examples

dbms_output.put_line(er.name || ' - ' || er.sal);end loop;end;beginfor i in (select deptno dname from dept)loopopen c_emp for select ename sal from emp where deptno i.deptno;dbms_output.put_line(i.dname);dbms_output.put_line('--------------');PrintEmployeeDetails;close c_emp; end loop;end;

15

Page 16: Ref Cursor Examples

REF CURSOR

A REF Cursor is a datatype that holds a cursor value in the same way that a VARCHAR2 variable will hold a string value.

A REF Cursor can be opened on the server and passed to the client as a unit rather than fetching one row at a time. One can use a Ref Cursor as target of an assignment, and it can be passed as parameter to other program units. Ref Cursors are opened with an OPEN FOR statement. In most other ways they behave similar to normal cursors.

This feature was introduced with PL/SQL v2.3 (Oracle 7.3).

Example

Create a function that opens a cursor and returns a reference to it:

CREATE OR REPLACE FUNCTION f RETURN SYS_REFCURSORAS c SYS_REFCURSOR;BEGIN OPEN c FOR select * from dual; RETURN c;END;

-----Call above function and fetch rows from the cursor it opened: set serveroutput onDECLARE c SYS_REFCURSOR; v VARCHAR2(1);BEGIN c := f(); -- Get ref cursor from function FETCH c into v; dbms_output.put_line('Value from cursor: '||v);END;--------=====================================

16

Page 17: Ref Cursor Examples

---======REF CURSOR USING IN FUNCTION ------

CREATE OR REPLACE FUNCTION func_ref_cur RETURN sys_refcursor AS p_cons_ref sys_refcursor; BEGINOPEN p_cons_ref FOR SELECT cons_ref,sdo_cd,acc_no FROM eaudit.EA_CONSUMER_CLUS WHERE sdo_cd = '1110' ;RETURN p_cons_ref; END;

---CALLING REF CURSOR FROM SQL PROMPTDECLARE ret_val sys_Refcursor; cons_ref VARCHAR2(10); sdo_cd VARCHAR2(4); acc_no VARCHAR2(4);BEGIN ret_val := func_ref_cur();LOOPFETCH RET_VAL INTO CONS_REF,SDO_CD,ACC_NO ;EXIT WHEN RET_VAL%NOTFOUND;DBMS_OUTPUT.PUT_LINE(CONS_REF||','||SDO_CD||','||ACC_NO);END LOOP;END;

17