Home » SQL & PL/SQL » SQL & PL/SQL » Cursor Variable : Ref Cursor Behavior (Oracle 11g, linux)
Cursor Variable : Ref Cursor Behavior [message #649207] Thu, 17 March 2016 06:35 Go to next message
Arunpandi
Messages: 4
Registered: March 2016
Junior Member
See the below example. In this select written for the cursor pcEmployee was given errors. Because of a Address was not found the employee in global table. But while executing this function it was not returning any error. while iteration of the result set(pcEmployee) it giving the error message.

Could any one can help to know why it is not raising the error while it is executing?

CREATE OR REPLACE PACKAGE PKG_EMP
AS

TYPE tEmployee IS RECORD (
Id 		M_EMPLOYEE.EMP_ID%TYPE,
Name	M_EMPLOYEE.EMP_NAME%TYPE
);

TYPE cEmployee IS REF CURSOR RETURN tEmployee;

FUNCTION fGetEmployeeDtls(
	pcEmployee 	OUT	cEmployee
) RETURN NUMBER;

END fGetEmployeeDtls;
/

CREATE OR REPLACE PACKAGE BODY PKG_EMP
AS

FUNCTION fGetEmployeeDtls(
	pcEmployee 	OUT	cEmployee
) RETURN NUMBER
	vReturn 	NUMBER DEFAULT 0;
BEGIN
	OPEN pcEmployee FOR
	SELECT 
		EMP_ID, 
		EMP_NAME
	FROM
		M_EMPLOYEE;
	RETURN vReturn;
EXCEPTION
	WHEN OTHERS THEN 
		vReturn := -1;
		RETURN vReturn;
END;

END fGetEmployeeDtls;
/


CREATE OR REPLACE FUNCTION fGetEmpAddress 
(
pEmpId 	IN 	M_EMPLOYEE.EMP_ID%TYPE
) RETURN VARCHAR2
AS
	pAddress	M_GLOBAL_ADDRESS.ADDRESS%TYPE;
BEGIN
	
	SELECT 
		Address
	INTO
		pAddress
	FROM 
		M_GLOBAL_ADDRESS
	WHERE
		EMP_ID = pEmpId;
EXCEPTION
	WHEN OTHERS THEN 
		RAISE_APPLICATION_ERROR(-20001, 'Address Not Found');
END;



[mod-edit: code tags added by bb]

[Updated on: Thu, 17 March 2016 13:43] by Moderator

Report message to a moderator

Re: Cursor Variable : Ref Cursor Behavior [message #649209 is a reply to message #649207] Thu, 17 March 2016 06:49 Go to previous messageGo to next message
Michel Cadot
Messages: 68644
Registered: March 2007
Location: Nanterre, France, http://...
Senior Member
Account Moderator

Welcome to the forum.
Please read OraFAQ Forum Guide and How to use [code] tags and make your code easier to read.

Use SQL*Plus and copy and paste your session, the WHOLE session.

Also read WHEN OTHERS.

Re: Cursor Variable : Ref Cursor Behavior [message #649210 is a reply to message #649209] Thu, 17 March 2016 10:15 Go to previous messageGo to next message
cookiemonster
Messages: 13920
Registered: September 2008
Location: Rainy Manchester
Senior Member
fGetEmployeeDtls doesn't call fGetEmpAddress in your example so I have no idea what error you expect to get from the ref cursor.
Re: Cursor Variable : Ref Cursor Behavior [message #649214 is a reply to message #649207] Thu, 17 March 2016 14:45 Go to previous messageGo to next message
Barbara Boehmer
Messages: 9090
Registered: November 2002
Location: California, USA
Senior Member
Your question is unclear and your code does not compile. Your package names at the top and the bottom of the package and package body must match and they don't. Also, you are missing AS or IS in your packaged function. I am guessing that you are using some other code that you have not posted to iterate through the result set of the packaged function and select from the other function. I am guessing that you probably want to return the value 'address not found' rather than raise it as an error. Otherwise, it will stop iterating at the first address that is not found. Please see the demonstration below. Please note that I am just trying to correct the functioning and am not suggesting anything as the best way. This is certainly a complicated way of doing what could be done with a simple select statement with an outer join and nvl.


SCOTT@orcl> -- tables for testing:
SCOTT@orcl> CREATE TABLE m_employee AS
  2  SELECT empno AS emp_id, ename AS emp_name
  3  FROM   emp
  4  /

Table created.

SCOTT@orcl> CREATE TABLE m_global_address AS
  2  SELECT empno AS emp_id, loc AS address
  3  FROM   emp, dept
  4  WHERE  emp.deptno = dept.deptno
  5  AND    emp.deptno != 10
  6  /

Table created.

SCOTT@orcl> -- package and function:
SCOTT@orcl> CREATE OR REPLACE PACKAGE PKG_EMP
  2  AS
  3    TYPE tEmployee IS RECORD
  4  	 (Id	     M_EMPLOYEE.EMP_ID%TYPE,
  5  	  Name	     M_EMPLOYEE.EMP_NAME%TYPE);
  6    TYPE cEmployee IS REF CURSOR RETURN tEmployee;
  7    FUNCTION fGetEmployeeDtls
  8  	 (pcEmployee	     OUT     cEmployee)
  9  	 RETURN NUMBER;
 10  END PKG_EMP;
 11  /

Package created.

SCOTT@orcl> SHOW ERRORS
No errors.
SCOTT@orcl> CREATE OR REPLACE PACKAGE BODY PKG_EMP
  2  AS
  3    FUNCTION fGetEmployeeDtls
  4  	 (pcEmployee	     OUT     cEmployee)
  5  	 RETURN NUMBER
  6    IS
  7  	 vReturn		     NUMBER DEFAULT 0;
  8    BEGIN
  9  	 OPEN pcEmployee FOR
 10  	   SELECT EMP_ID, EMP_NAME
 11  	   FROM   M_EMPLOYEE;
 12  	 RETURN vReturn;
 13    EXCEPTION
 14  	 WHEN OTHERS THEN
 15  	   vReturn := -1;
 16  	   RETURN vReturn;
 17  	 END fGetEmployeeDtls;
 18  END PKG_EMP;
 19  /

Package body created.

SCOTT@orcl> SHOW ERRORS
No errors.
SCOTT@orcl> CREATE OR REPLACE FUNCTION fGetEmpAddress
  2    (pEmpId	     IN      M_EMPLOYEE.EMP_ID%TYPE)
  3    RETURN VARCHAR2
  4  AS
  5    pAddress      M_GLOBAL_ADDRESS.ADDRESS%TYPE;
  6  BEGIN
  7    SELECT Address
  8    INTO   pAddress
  9    FROM   M_GLOBAL_ADDRESS
 10    WHERE  EMP_ID = pEmpId;
 11    RETURN pAddress;
 12  EXCEPTION
 13    WHEN NO_DATA_FOUND THEN
 14  	 RETURN ('Address Not Found');
 15    WHEN OTHERS THEN
 16  	 RAISE_APPLICATION_ERROR(-20001, 'Address Not Found');
 17  END fGetEmpAddress;
 18  /

Function created.

SCOTT@orcl> SHOW ERRORS
No errors.
SCOTT@orcl> -- iterating through cursor using function:
SCOTT@orcl> DECLARE
  2    v_num   NUMBER;
  3    v_name  VARCHAR2(15);
  4    v_ref   SYS_REFCURSOR;
  5    v_id    NUMBER;
  6  BEGIN
  7    v_num := pkg_emp.fGetEmployeeDtls (v_ref);
  8    LOOP
  9  	 FETCH v_ref INTO v_num, v_name;
 10  	 EXIT WHEN v_ref%NOTFOUND;
 11  	 DBMS_OUTPUT.PUT_LINE (v_num || ' ' || v_name || ' ' || fGetEmpAddress(v_num));
 12    END LOOP;
 13    CLOSE v_ref;
 14  END;
 15  /
7369 SMITH DALLAS
7499 ALLEN CHICAGO
7521 WARD CHICAGO
7566 JONES DALLAS
7654 MARTIN CHICAGO
7698 BLAKE CHICAGO
7782 CLARK Address Not Found
7788 SCOTT DALLAS
7839 KING Address Not Found
7844 TURNER CHICAGO
7876 ADAMS DALLAS
7900 JAMES CHICAGO
7902 FORD DALLAS
7934 MILLER Address Not Found

PL/SQL procedure successfully completed.

SCOTT@orcl> -- simpler way:
SCOTT@orcl> SELECT m_employee.emp_id, emp_name, NVL (address, 'Address Not Found')
  2  FROM   m_employee, m_global_address
  3  WHERE  m_employee.emp_id = m_global_address.emp_id(+)
  4  ORDER  BY emp_id
  5  /

    EMP_ID EMP_NAME   NVL(ADDRESS,'ADDR
---------- ---------- -----------------
      7369 SMITH      DALLAS
      7499 ALLEN      CHICAGO
      7521 WARD       CHICAGO
      7566 JONES      DALLAS
      7654 MARTIN     CHICAGO
      7698 BLAKE      CHICAGO
      7782 CLARK      Address Not Found
      7788 SCOTT      DALLAS
      7839 KING       Address Not Found
      7844 TURNER     CHICAGO
      7876 ADAMS      DALLAS
      7900 JAMES      CHICAGO
      7902 FORD       DALLAS
      7934 MILLER     Address Not Found

14 rows selected.

Re: Cursor Variable : Ref Cursor Behavior [message #649217 is a reply to message #649214] Fri, 18 March 2016 00:03 Go to previous messageGo to next message
Arunpandi
Messages: 4
Registered: March 2016
Junior Member
Sorry for the wrong code. Please find the below code.

DROP TABLE M_GLOBAL_ADDRESS;
DROP TABLE M_EMPLOYEE;

CREATE TABLE M_EMPLOYEE (
	EMP_ID   	NUMBER(10),
	EMP_NAME	VARCHAR2(100 CHAR)
);
/
ALTER TABLE M_EMPLOYEE ADD CONSTRAINT PK_EMP_ID PRIMARY KEY (EMP_ID);
CREATE TABLE M_GLOBAL_ADDRESS (
	G_ADD_ID	NUMBER(10),
	EMP_ID   	NUMBER(10),
	ADDRESS		VARCHAR2(500 CHAR)
);
/
ALTER TABLE M_GLOBAL_ADDRESS ADD CONSTRAINT PK_G_ADD_ID PRIMARY KEY (G_ADD_ID);
ALTER TABLE M_GLOBAL_ADDRESS ADD CONSTRAINT FK_G_ADD_EMP_ID FOREIGN KEY (EMP_ID) REFERENCES M_EMPLOYEE(EMP_ID);

INSERT INTO M_EMPLOYEE VALUES (1, 'Arun');
INSERT INTO M_EMPLOYEE VALUES (2, 'Pandi');

INSERT INTO M_GLOBAL_ADDRESS VALUES (1, 1, 'Address 1');

CREATE OR REPLACE FUNCTION fGetEmpAddress 
(
	pEmpId 	IN 	M_EMPLOYEE.EMP_ID%TYPE
) RETURN VARCHAR2
AS
	pAddress	M_GLOBAL_ADDRESS.ADDRESS%TYPE;
BEGIN
	
	SELECT 
		Address
	INTO
		pAddress
	FROM 
		M_GLOBAL_ADDRESS
	WHERE
		EMP_ID = pEmpId;
	RETURN pAddress;
EXCEPTION
	WHEN OTHERS THEN 
		RAISE_APPLICATION_ERROR(-20001, 'Address Not Found');
END fGetEmpAddress;
/

SHOW ERRORS FUNCTION fGetEmpAddress
/
 
CREATE OR REPLACE PACKAGE PKG_EMP
AS
	TYPE tEmployee IS RECORD (
		Id 		M_EMPLOYEE.EMP_ID%TYPE,
		Name	M_EMPLOYEE.EMP_NAME%TYPE,
		Address	M_GLOBAL_ADDRESS.ADDRESS%TYPE
	);

	TYPE cEmployee IS REF CURSOR RETURN tEmployee;

	FUNCTION fGetEmployeeDtls(
		pcEmployee 	OUT	cEmployee
	) RETURN NUMBER;

END PKG_EMP;
/

SHOW ERRORS PACKAGE PKG_EMP
/

CREATE OR REPLACE PACKAGE BODY PKG_EMP
AS
	FUNCTION fGetEmployeeDtls(
		pcEmployee 	OUT	cEmployee
	) RETURN NUMBER
  AS 
		vReturn NUMBER(1) := 0;
	BEGIN
		OPEN pcEmployee FOR
		SELECT 
			EMP_ID, 
			EMP_NAME, 
			fGetEmpAddress(EMP_ID)
		FROM
			M_EMPLOYEE;
		RETURN vReturn;
	EXCEPTION
		WHEN OTHERS THEN 
			vReturn := 1;
			RETURN vReturn;
	END fGetEmployeeDtls;
END PKG_EMP;
/

SHOW ERRORS PACKAGE BODY PKG_EMP
/

If you are running the below select you will get the error. 

SELECT 
	EMP_ID, 
	EMP_NAME, 
	fGetEmpAddress(EMP_ID)
FROM
	M_EMPLOYEE;

ORA-20001: Address Not Found
ORA-06512: at "RFP_ADM.FGETEMPADDRESS", line 20

Same query was i used for a cursor variable in a function. while executing the function i can get the error. 
DECLARE
    vRet NUMBER(1);
    pcEmployee 	PKG_EMP.cEmployee;
BEGIN 
    vRet := PKG_EMP.fGetEmployeeDtls(pcEmployee);
    
    DBMS_OUTPUT.PUT_LINE(vRet);
END;
/


It seems query was not executed at the time of function execution.
While trying to access the records in the cursor pcEmployee i am getting the error.

What i have to do if i want to get the error while executing the function?

Thanks in advance.


[mod-edit: code tags added by bb; please start adding them yourself]

[Updated on: Fri, 18 March 2016 01:37] by Moderator

Report message to a moderator

Re: Cursor Variable : Ref Cursor Behavior [message #649221 is a reply to message #649217] Fri, 18 March 2016 01:33 Go to previous messageGo to next message
Michel Cadot
Messages: 68644
Registered: March 2007
Location: Nanterre, France, http://...
Senior Member
Account Moderator
Michel Cadot wrote on Thu, 17 March 2016 12:49

Welcome to the forum.
Please read OraFAQ Forum Guide and How to use [code] tags and make your code easier to read.

Use SQL*Plus and copy and paste your session, the WHOLE session.

Also read WHEN OTHERS.

Re: Cursor Variable : Ref Cursor Behavior [message #649228 is a reply to message #649217] Fri, 18 March 2016 03:03 Go to previous messageGo to next message
Barbara Boehmer
Messages: 9090
Registered: November 2002
Location: California, USA
Senior Member
The exception that is being raised, which is being handled by your WHEN OTHERS clause is actually NO_DATA_FOUND. There is a note about this in some older documentation. Please see the link (click then scroll down) and excerpt below. I could not find a similar note in more recent documentation, but I may have missed it. What I believe it means, in your case, is that, if your fGetEmpAddress causes a no_data_found exception, regardless of how you handle it, such as using when others, then the error may or may not be propagated back to whatever called it. So, when you use it in a simple select statement by itself, the error may be propagated, but when that same select statement is used within another function like your fGetEmployeeDtls, then it may not be propagated.

https://docs.oracle.com/cd/B13789_01/appdev.101/b10807/07_errs.htm#i9355

"NO_DATA_FOUND A SELECT INTO statement returns no rows, or your program references a deleted element in a nested table or an uninitialized element in an index-by table.

Because this exception is used internally by some SQL functions to signal that they are finished, you should not rely on this exception being propagated if you raise it within a function that is called as part of a query."

[Updated on: Fri, 18 March 2016 03:08]

Report message to a moderator

Re: Cursor Variable : Ref Cursor Behavior [message #649231 is a reply to message #649228] Fri, 18 March 2016 04:46 Go to previous messageGo to next message
cookiemonster
Messages: 13920
Registered: September 2008
Location: Rainy Manchester
Senior Member
What you are describing Barbara can't happen in this case because the WHEN OTHERS catches the error locally and transforms it into -20001
Actually the issue is that the function isn't run when you open the ref cursor.
Opening a ref cursor doesn't cause oracle to go and execute the query and store the result somewhere. It only starts executing the query when you fetch from the ref cursor. So the function isn't executed till that happens.
It becomes clear if I modify the function slightly:
CREATE OR REPLACE FUNCTION fGetEmpAddress 
(
  pEmpId   IN   M_EMPLOYEE.EMP_ID%TYPE
) RETURN VARCHAR2
AS
  pAddress  M_GLOBAL_ADDRESS.ADDRESS%TYPE;
BEGIN
  
  SELECT 
    Address
  INTO
    pAddress
  FROM 
    M_GLOBAL_ADDRESS
  WHERE
    EMP_ID = pEmpId;
  dbms_output.put_line('running function at '||to_char(sysdate, 'HH24:MI:SS'));
  RETURN pAddress;
EXCEPTION
  WHEN OTHERS THEN 
    RAISE_APPLICATION_ERROR(-20001, 'Address Not Found');
END fGetEmpAddress;
/

Then in sqlplus declare a ref cursor variable and open the ref cursor:
SQL> var x refcursor
SQL> set serveroutput on
SQL> DECLARE
    vRet NUMBER(1);
BEGIN

    vRet := PKG_EMP.fGetEmployeeDtls(:x);
  dbms_output.put_line('ref cursor open at '||to_char(sysdate, 'HH24:MI:SS'));
END;
/  2    3    4    5    6    7    8
ref cursor open at 09:35:53

PL/SQL procedure successfully completed.


Note the dbms_output I added to the function hasn't been run. Now lets fetch from the ref cursor:
SQL> print :x
ERROR:
ORA-20001: Address Not Found
ORA-06512: at "LIVE.FGETEMPADDRESS", line 21



no rows selected

running function at 09:35:59

That runs the function and throws the error.

If we remove the exception handler from the function:
CREATE OR REPLACE FUNCTION fGetEmpAddress 
(
  pEmpId   IN   M_EMPLOYEE.EMP_ID%TYPE
) RETURN VARCHAR2
AS
  pAddress  M_GLOBAL_ADDRESS.ADDRESS%TYPE;
BEGIN
  
  SELECT 
    Address
  INTO
    pAddress
  FROM 
    M_GLOBAL_ADDRESS
  WHERE
    EMP_ID = pEmpId;
  dbms_output.put_line('running function at '||to_char(sysdate, 'HH24:MI:SS'));
  RETURN pAddress;
END fGetEmpAddress;
/

And re-execute in sqlplus:
SQL> DECLARE
    vRet NUMBER(1);
BEGIN

    vRet := PKG_EMP.fGetEmployeeDtls(:x);
  dbms_output.put_line('ref cursor open at '||to_char(sysdate, 'HH24:MI:SS'));
END;
/  2    3    4    5    6    7    8
ref cursor open at 09:44:09

PL/SQL procedure successfully completed.

SQL> print :x

    EMP_ID
----------
EMP_NAME
--------------------------------------------------------------------------------
FGETEMPADDRESS(EMP_ID)
--------------------------------------------------------------------------------
         1
Arun
Address 1

         2
Pandi


    EMP_ID
----------
EMP_NAME
--------------------------------------------------------------------------------
FGETEMPADDRESS(EMP_ID)
--------------------------------------------------------------------------------


running function at 09:44:14
SQL>

We now get the behaviour Barbara described
Re: Cursor Variable : Ref Cursor Behavior [message #649241 is a reply to message #649231] Fri, 18 March 2016 12:09 Go to previous messageGo to next message
Barbara Boehmer
Messages: 9090
Registered: November 2002
Location: California, USA
Senior Member
Thanks for the explanation and demonstration.
Re: Cursor Variable : Ref Cursor Behavior [message #649270 is a reply to message #649241] Mon, 21 March 2016 00:35 Go to previous messageGo to next message
Arunpandi
Messages: 4
Registered: March 2016
Junior Member
Thank you so much to cookiemonster & Barbara Boehmer for your explanation.

My expectation is not avoiding the error. It should raise the error while execution.

Could you please explain why ref cursors are not executing at the time of function/procedure execution?
And if i want to execute the select given in open <cursor> for <select>, is there any way to achieve that?
Is it possible?

Please correct me if i am wrong.

Thanks in advance.



Re: Cursor Variable : Ref Cursor Behavior [message #649283 is a reply to message #649270] Mon, 21 March 2016 03:33 Go to previous messageGo to next message
cookiemonster
Messages: 13920
Registered: September 2008
Location: Rainy Manchester
Senior Member
It's just the way oracle works, you get the same behaviour with explicit cursors.
Fundamentally cursors aren't actually executed until you start fetching.
Re: Cursor Variable : Ref Cursor Behavior [message #649316 is a reply to message #649283] Tue, 22 March 2016 09:20 Go to previous message
Bill B
Messages: 1971
Registered: December 2004
Senior Member
I'm curious, why are you going out of your way to do your ref cursor the most complicated way you can. As Barbara showed a ref cursor using a simple select will work much faster and cleaner

CREATE OR REPLACE PACKAGE BODY PKG_EMP
AS
	FUNCTION fGetEmployeeDtls(
		pcEmployee 	OUT	cEmployee
	) RETURN NUMBER
  AS 
	BEGIN
		OPEN pcEmployee FOR
		SELECT 
			EMP_ID, 
			EMP_NAME, 
			NVL (address, 'Address Not Found') address
		FROM   m_global_address b,
			M_EMPLOYEE a
            where a.emp_id = b.emp_id(+)
            order by a.emp_id;
		RETURN 0;
	EXCEPTION
		WHEN OTHERS THEN 
    		RETURN 1;
	END fGetEmployeeDtls;
END PKG_EMP;

[Updated on: Tue, 22 March 2016 09:24]

Report message to a moderator

Previous Topic: Split the Sum in 03 Portions
Next Topic: Date Functions - Finding the quarter of the year
Goto Forum:
  


Current Time: Wed Apr 24 02:24:53 CDT 2024