Stored Procedure - not able to display no data found message - oracle

I have created a procedure, which will take the Mgr number as input for execution.
When ever data is there for input passed Mgr number we are getting data. But when we don't have data it is not displaying "no data found" message.
create or replace procedure sp1 (mg number)
as
cursor c1 is select * from emp1 where mgr = mg;
i emp1%rowtype;
begin
for i in c1
loop
dbms_output.put_line(i.ename||' '||i.sal);
end loop;
exception
when no_data_found then
dbms_output.put_line('no data found');
end;
/
Can anyone please tell me how to display "no data found" message when there is no data for input Mgr number.
Thanks,
Subash

Cursor can't return no_data_found, pure select can.
Here's one workaround: declare a local counter variable and see whether it changed.
SQL> create or replace procedure sp1 (mg number) as
2 cursor c1 is select * from emp where mgr = mg;
3 i emp%rowtype;
4 l_cnt number := 0; --> this
5 begin
6 for i in c1 loop
7 l_cnt := l_cnt + 1; --> this
8 dbms_output.put_line(i.ename ||' '|| i.sal);
9 end loop;
10 if l_cnt = 0 then --> this
11 dbms_output.put_line('no data found');
12 end if;
13 end;
14 /
Procedure created.
Testing:
SQL> exec sp1(7698);
ALLEN 1600
WARD 1250
MARTIN 1250
TURNER 1500
JAMES 950
PL/SQL procedure successfully completed.
SQL> exec sp1(76982);
no data found
PL/SQL procedure successfully completed.
SQL>
If you want to do everything manually, then you have to declare cursor, cursor variable, open cursor, open loop, fetch, exit, close loop, close cursor:
SQL> create or replace procedure sp1 (mg number) as
2 cursor c1 is select * from emp where mgr = mg;
3 i emp%rowtype;
4 begin
5 open c1;
6 loop
7 fetch c1 into i;
8 exit when c1%notfound;
9 dbms_output.put_line(i.ename ||' '|| i.sal);
10 end loop;
11 close c1;
12 end;
13 /
Procedure created.
SQL> exec sp1(7698);
ALLEN 1600
WARD 1250
MARTIN 1250
TURNER 1500
JAMES 950
PL/SQL procedure successfully completed.
SQL>
By far the simplest option is a cursor for loop as Oracle does mostly everything for you:
create or replace procedure sp1 (mg number) as
begin
for c1 in (select * from emp where mgr = mg) loop
dbms_output.put_line(c1.ename ||' '|| c1.sal);
end loop;
end;
/
exec sp1(7698);

Related

How to return more than one select queries in same procedure

I would like to ask how can i print output in procedure more than one statement.
Assume that you want to show dba_objects and segments row count. But i can not use dbms_sql.return_result my version is 11g.
Something like,
create or replace procedure get_rows_count
(
cursor1 out SYS_REFCURSOR,
cursor2 out SYS_REFCURSOR
)
as
begin
open cursor1 for select count(*) from dba_objects;
open cursor2 for select count(*) from dba_segments;
end get_rows_count;
/
Assume that you want to show dba_objects and segments row count
I assumed it. Conclusion: that's not the way to do it. If you want to get row count from e.g. dba_objects, then you should just
select count(*) from dba_objects;
in any variation you want (pure SQL, function that returns that number, procedure with an OUT parameter (worse option), ...). But, creating a procedure which uses ref cursor for that purpose is ... well, wrong.
If I got you wrong, then: procedure you wrote is OK. You can call it from another PL/SQL procedure (named or anonymous), fetch result into a variable and do something with it (e.g. display it).
Your procedure (selects from Scott's tables; I don't have access to DBA_ views):
SQL> CREATE OR REPLACE PROCEDURE get_rows_count (cursor1 OUT SYS_REFCURSOR,
2 cursor2 OUT SYS_REFCURSOR)
3 AS
4 BEGIN
5 OPEN cursor1 FOR SELECT * FROM emp;
6
7 OPEN cursor2 FOR SELECT * FROM dept;
8 END get_rows_count;
9 /
Procedure created.
How to call it? See line #8:
SQL> SET SERVEROUTPUT ON
SQL>
SQL> DECLARE
2 rc1 SYS_REFCURSOR;
3 rc2 SYS_REFCURSOR;
4 --
5 rw1 emp%ROWTYPE;
6 rw2 dept%ROWTYPE;
7 BEGIN
8 get_rows_count (rc1, rc2);
9
10 DBMS_OUTPUT.put_line ('Employees -----------');
11
12 LOOP
13 FETCH rc1 INTO rw1;
14
15 EXIT WHEN rc1%NOTFOUND;
16
17 DBMS_OUTPUT.put_line (rw1.ename);
18 END LOOP;
19
20 --
21 DBMS_OUTPUT.put_line ('Departments ---------');
22
23 LOOP
24 FETCH rc2 INTO rw2;
25
26 EXIT WHEN rc2%NOTFOUND;
27
28 DBMS_OUTPUT.put_line (rw2.dname);
29 END LOOP;
30
31 DBMS_OUTPUT.put_line ('First ref cursor: ' || rc1%ROWCOUNT);
32 DBMS_OUTPUT.put_line ('Second ref cursor: ' || rc2%ROWCOUNT);
33 END;
34 /
Result:
Employees -----------
SMITH
ALLEN
WARD
JONES
MARTIN
BLAKE
CLARK
SCOTT
KING
TURNER
ADAMS
JAMES
FORD
MILLER
Departments ---------
ACCOUNTING
RESEARCH
SALES
OPERATIONS
First ref cursor: 14
Second ref cursor: 4
PL/SQL procedure successfully completed.
SQL>
You can use famous DBMS_OUTPUT.PUT_LINE() along with %ROWCOUNT suffix for your case such as
SET serveroutput ON
CREATE OR REPLACE PROCEDURE get_rows_count(
cursor1 OUT SYS_REFCURSOR,
cursor2 OUT SYS_REFCURSOR,
count1 OUT INT,
count2 OUT INT
) AS
cur_rec_obj user_objects%ROWTYPE;
cur_rec_seg user_segments%ROWTYPE;
BEGIN
OPEN cursor1 FOR SELECT * FROM user_objects;
LOOP
FETCH cursor1 INTO cur_rec_obj;
EXIT WHEN cursor1%NOTFOUND;
END LOOP;
OPEN cursor2 FOR SELECT * FROM user_segments;
LOOP
FETCH cursor2 INTO cur_rec_seg;
EXIT WHEN cursor2%NOTFOUND;
END LOOP;
count1 := cursor1%ROWCOUNT;
count2 := cursor2%ROWCOUNT;
DBMS_OUTPUT.PUT_LINE(count1);
DBMS_OUTPUT.PUT_LINE(count2);
END;
/
and you can call as follows from the SQL Window of PL/SQL Developer :
DECLARE
v_cursor1 SYS_REFCURSOR;
v_cursor2 SYS_REFCURSOR;
v_count1 INT;
v_count2 INT;
BEGIN
get_rows_count(v_cursor1, v_cursor2, v_count1, v_count2 );
END;
/

Cursor FOR LOOP with cursor as parameter

I have the following procedure executing some business logic (looping through a cursor):
PROCEDURE myproc() AS
CURSOR mycur IS
SELECT * FROM mytable;
BEGIN
FOR c IN mycur LOOP
...business logic here...
...many lines of code...
END LOOP;
END myproc;
I'd like to have different procedures and execute the same business logic for different cursors (sets of data). For example I'd like to have one procedure myproc_adopters() for:
SELECT * FROM mytable WHERE cutomer_type='Adopters'
and another procedure myproc_others() for others:
SELECT * FROM mytable WHERE customer_type!='Adopters'
So I'd like to have one main procedure mainproc() containing cursor loop and business logic and other procedures calling this main procedure and sending different cursors as parameters. The problem is that it seems that cursor FOR loop does not accept cursor as variable that I can send as procedure call parameter:
PROCEDURE myproc_adopters() AS
CURSOR mycur IS
SELECT * FROM mytable WHERE customer_type='Adopters';
BEGIN
mainproc(mycur);
END myproc_adopters;
PROCEDURE myproc_others() AS
CURSOR mycur IS
SELECT * FROM mytable WHERE customer_type!='Adopters';
BEGIN
mainproc(mycur);
END myproc_others;
PROCEDURE mainproc(mycur IN SYS_REFCURSOR) AS
BEGIN
FOR c IN mycur LOOP <-- does not accept cursor as variable
...
END LOOP;
END mainproc;
How to send different cursor to the same cursor FOR LOOP?
Your idea is OK (at least, to me), but - you then have to pass refcursor (as you declared it).
For example:
mainproc:
SQL> create or replace procedure mainproc (mycur in sys_refcursor)
2 is
3 l_row emp%rowtype;
4 begin
5 loop
6 fetch mycur into l_row;
7 exit when mycur%notfound;
8
9 dbms_output.put_line(l_row.ename ||' - '|| l_row.job ||' - '|| l_row.sal);
10 end loop;
11 end;
12 /
Procedure created.
This procedure accepts a parameter and - depending on it - opens refcursor with some criteria and then calls mainproc, passing that refcursor:
SQL> create or replace procedure myproc_adopters (par_deptno in emp.deptno%type) is
2 l_rc sys_refcursor;
3 begin
4 if par_deptno = 10 then
5 open l_rc for select * from emp where job = 'CLERK';
6 elsif par_deptno = 20 then
7 open l_rc for select * from emp where sal > 2000;
8 end if;
9
10 mainproc (l_rc);
11 close l_rc;
12 end;
13 /
Procedure created.
Testing:
SQL> set serveroutput on
SQL> exec myproc_adopters(10);
SMITH - CLERK - 840
ADAMS - CLERK - 1100
JAMES - CLERK - 950
MILLER - CLERK - 1300
PL/SQL procedure successfully completed.
SQL>
SQL> exec myproc_adopters(20);
JONES - MANAGER - 2975
BLAKE - MANAGER - 2850
CLARK - MANAGER - 2450
SCOTT - ANALYST - 3000
KING - PRESIDENT - 5000
FORD - ANALYST - 3000
PL/SQL procedure successfully completed.
SQL>
There are two other ways you could do this that don't require a refcursor:
Add a parameter to your myproc procedure, and update the cursor to retrieve the rows based on the parameter, e.g.:
PROCEDURE myproc(p_query_type IN VARCHAR2) AS
CURSOR mycur IS
SELECT *
FROM mytable
WHERE (p_query_type IS NULL -- or whatever value you want to indicate all rows, e.g. p_query_type = 'ALL'
OR (UPPER(p_query_type) = 'ADOPTERS' AND customer_type = 'Adopters')
OR (UPPER(p_query_type) = 'OTHERS' AND customer_type != 'Adopters'));
BEGIN
FOR c IN mycur LOOP
...business logic here...
...many lines of code...
END LOOP;
END myproc;
Or you could extract the business logic into its own procedure (complete with the requisite number of parameters), and the wrapper procedures call that within the cursor-for-loop, e.g.:
PROCEDURE mainproc (p_param1 mytable.col1%TYPE,
p_param2 mytable.col2%TYPE,
....)
BEGIN
...business logic here...
...many lines of code...
END mainproc;
PROCEDURE myproc_adopters() AS
CURSOR mycur IS
SELECT * FROM mytable WHERE customer_type='Adopters';
BEGIN
FOR c IN mycur LOOP
mainproc (p_param1 => c.col1,
p_param2 => c.col2,
...)
END LOOP;
END myproc_adopters;
PROCEDURE myproc_others() AS
CURSOR mycur IS
SELECT * FROM mytable WHERE customer_type!='Adopters';
BEGIN
FOR c IN mycur LOOP
mainproc (p_param1 => c.col1,
p_param2 => c.col2,
...)
END LOOP;
END myproc_others;
I hope that these procedures are in a package! If you're going to go down the route of having wrapper procedures but your current procedure isn't in a package, I highly recommend you move it into a package.
My personal preference would be to go with a combination of both options; have the cursor decide what rows to select based on the input parameter, but also encapsulate the business logic in its own procedure, or better yet, break it down into separate procedures to make it more manageable, as one long, spaghetti-like procedure isn't good programming practice (IMO).

Trying to create SELECT Stored Procedure in Oracle PL SQL and getting error in VARIABLE CURSOR_OUTPUT REFCURSOR;

I have created procedure for simple select statement output.
CREATE OR REPLACE PROCEDURE marcydashboard (p_recordset IN OUT SYS_REFCURSOR)
IS
BEGIN
OPEN p_recordset FOR select employee_id,first_name,last_name from employees;
END;
VARIABLE CURSOR_OUTPUT REFCURSOR;
EXECUTE marcydashboard(:CURSOR_OUTPUT);
I need result same like select statement result but am getting error like:
ERROR at line 6: PLS-00103: Encountered the symbol "VARIABLE"
4. END;
5. VARIABLE CURSOR_OUTPUT REFCURSOR;
6. EXECUTE marcydashboard(:CURSOR_OUTPUT );
please help me to fix the error
Well, it works in SQL*Plus:
SQL> CREATE OR REPLACE PROCEDURE marcydashboard (p_recordset OUT SYS_REFCURSOR)
2 IS
3 BEGIN
4 OPEN p_recordset FOR
5 SELECT employee_id, first_name, last_name FROM employees;
6 END;
7 /
Procedure created.
SQL>
SQL> VARIABLE cursor_output REFCURSOR;
SQL> EXECUTE marcydashboard(:cursor_output);
PL/SQL procedure successfully completed.
SQL> PRINT :cursor_output
EMPLOYEE_ID FIRST_NAME LAST_NAME
----------- ---------- ---------
7369 SMITH CLERK
7499 ALLEN SALESMAN
7521 WARD SALESMAN
7566 JONES MANAGER
SQL>
Looks like you aren't using that tool but some other. In that case, it won't work and you'll have to do it differently, e.g.
SQL> SET SERVEROUTPUT ON
SQL>
SQL> DECLARE
2 l_rc SYS_REFCURSOR;
3 --
4 l_employee_id employees.employee_id%TYPE;
5 l_first_name employees.first_name%TYPE;
6 l_last_name employees.last_name%TYPE;
7 BEGIN
8 marcydashboard (l_rc);
9
10 LOOP
11 FETCH l_rc INTO l_employee_id, l_first_name, l_last_name;
12
13 EXIT WHEN l_rc%NOTFOUND;
14
15 DBMS_OUTPUT.put_line (
16 l_employee_id || ' - ' || l_first_name || ' - ' || l_last_name);
17 END LOOP;
18
19 CLOSE l_rc;
20 END;
21 /
7369 - SMITH - CLERK
7499 - ALLEN - SALESMAN
7521 - WARD - SALESMAN
7566 - JONES - MANAGER
PL/SQL procedure successfully completed.
SQL>
You need a / on a new line after END;
END;
/
VARIABLE CURSOR_OUTPUT REFCURSOR;

Single cursor for two different SQL in Oracle

I have a scenario where i want to use a cursor for two different sql.
create or replace procedure(p_load_type in number)
as
create cursor prod_cur
is
/* This is the 1st SQL*/
select product_id,
product_name
from Test_product;
prod_rec prod_cur%rowtype;
/* here is the second sql that i want cursor open for this when p_load_type=2
select product_id,
product_name
from Test_product tp,
Test_delta_cases tdc
where tp.product_id=tdc.product_id;*/
begin
if p_load_type=1 then /* opening for 1st sql*/
open prod_cur
elsif p_load_type=2 then /* opening for 2nd sql*/
open prod_cur
end if;
loop
some logic............
end loop;
end;
Please use formatting to format your code in your question. Anyway, you can do with REF CURSORS, eg
SQL> create or replace
2 procedure P(p_type int ) is
3 rc sys_refcursor;
4
5 cursor c_template is select 1 x, 1 y from dual;
6 l_rows c_template%rowtype;
7
8 begin
9 if p_type = 1 then
10 open rc for select empno, deptno from scott.emp;
11 else
12 open rc for select empno, sal from scott.emp;
13 end if;
14
15 loop
16 fetch rc into l_rows;
17 exit when rc%notfound;
18 dbms_output.put_line(l_rows.x||','||l_rows.y);
19 end loop;
20 end;
21 /
Procedure created.
SQL> set serverout on
SQL> exec p(1);
7369,20
7499,30
7521,30
7566,20
7654,30
7698,30
7782,10
7788,20
7839,10
7844,30
7876,20
7900,30
7902,20
7934,10
PL/SQL procedure successfully completed.
SQL> exec p(2);
7369,800
7499,1600
7521,1250
7566,2975
7654,1250
7698,2850
7782,2450
7788,3000
7839,5000
7844,1500
7876,1100
7900,950
7902,3000
7934,1300
PL/SQL procedure successfully completed.

Oracle PLSQL BULK Collect and For Loop

I have written the following oracle procedure to fetch data in bulk and process it in blocks. I am using the bulk collect option with limit to fetch the data. But inside for loop i am not able to retrieve the ORD_ID. I am trying to output the ORD_ID using
DBMS_OUTPUT.put_line(l_orders(indx));
But getting compilation error "wrong number or type of arguments in call to 'PUT_LINE'"
create or replace PROCEDURE TESTPROC AS
CURSOR order_id_cur IS SELECT ORD_ID FROM orders ORDER BY ORD_ID ASC;
l_order_id VARCHAR2(100);
TYPE orders_aat IS TABLE OF order_id_cur%ROWTYPE;
l_orders orders_aat;
limit_in NUMBER :=10;
batch_in NUMBER :=0;
BEGIN
OPEN order_id_cur;
LOOP
FETCH order_id_cur
BULK COLLECT INTO l_orders LIMIT limit_in;
DBMS_OUTPUT.put_line('Batch-----'||batch_in);
FOR indx IN 1 .. l_orders.COUNT
LOOP
DBMS_OUTPUT.put_line(indx);
DBMS_OUTPUT.put_line(l_orders(indx));
END LOOP;
EXIT WHEN l_orders.COUNT < limit_in;
batch_in := batch_in+1;
END LOOP;
CLOSE order_id_cur;
END TESTPROC;
How can i get the values of ORD_ID inside the for loop.
Do it like this -
DBMS_OUTPUT.put_line(l_orders(indx).ORD_ID);
For example,
SQL> DECLARE
2 type t
3 IS
4 TABLE OF emp%rowtype;
5 a t;
6 BEGIN
7 SELECT * BULK COLLECT INTO a FROM emp;
8 FOR i IN 1..a.count
9 LOOP
10 dbms_output.put_line (a(i).ename);
11 END LOOP;
12 END;
13 /
SMITH
ALLEN
WARD
JONES
MARTIN
BLAKE
CLARK
SCOTT
KING
TURNER
ADAMS
JAMES
FORD
MILLER
PL/SQL procedure successfully completed.
SQL>
you can also make loop directly to cursor like below
FOR recc in order_id_cur
LOOP
DBMS_OUTPUT.put_line(recc.ORD_ID );
END LOOP;

Resources