CURSOR IN PROCEDURE PL/SQL - oracle

i have successfully created procedure for checking my stock item, here's the syntax :
create or replace procedure check_stock
AS
CURSOR check_stock IS
select category.category_name, item.item_name, item.stock from category join item on category.category_id = item.category_id;
begin
for stock in check_stock
LOOP
DBMS_OUTPUT.PUT_LINE(stock.category_name||' '||stock.item_name||' '||stock.stock);
END LOOP;
End;
now i want to create the same procedure but i need to input the item name so the output is the stock of that item name i have inputted, can someone show me the way/syntax using the syntax i've given above ? thanks

create or replace procedure check_stock ( v_item_name in Integer )
AS
CURSOR check_stock IS
select category.category_name, item.item_name, item.stock from category join item on category.category_id = item.category_id where item.item_name = v_item_name ;
begin
for stock in check_stock
LOOP
DBMS_OUTPUT.PUT_LINE(stock.category_name||' '||stock.item_name||' '||stock.stock);
END LOOP;
End;

You need to use one IN and one OUT parameter :
SQL> SET SERVEROUTPUT ON
SQL> CREATE OR REPLACE PROCEDURE check_stock(
i_item_name in item.item_name%type,
o_stock out category.stock%type
) AS
CURSOR check_stock IS
SELECT c.category_name, i.item_name, i.stock
FROM category c
JOIN item i
ON c.category_id = i.category_id
WHERE i.item_name = i_item_name;
BEGIN
FOR stock IN check_stock
LOOP
DBMS_OUTPUT.PUT_LINE(stock.category_name || ' ' || stock.item_name || ' ' || stock.stock);
o_stock := nvl(o_stock,0) + stock.stock;
END LOOP;
END;
/
but this way, you get the last value of stock from the cursor for multiple returning rows. It's unclear which value for stock value. So, I considered the summing up the returning stock values.

Related

function not returning a value for no rows returned

I created a function that takes a movie id as input and returns stock information based from the ID. The function mostly works but if I want to retrieve information from a movie that is not in the database(returns no rows) nothing returns. Can't figure out why?
doesn't give me an error when i call an ID that returns no rows so exception handling wouldn't work.
create or replace function stock_info
(p_id IN NUMBER
)
return VARCHAR2
IS
cursor c1 is
select movie_id, movie_title, movie_qty
from mm_movie
where p_id = movie_id;
lv_movie_info VARCHAR2(100);
BEGIN
for i in c1 loop
if p_id = i.movie_id then
lv_movie_info := i.movie_title || ' is available: ' || i.movie_qty || ' on the shelf';
else
lv_movie_info := 'no data found';
end if;
end loop;
return lv_movie_info;
END STOCK_INFO;
/
The reason you don't get anything when there is no data is that the loop doesn't execute. Logically the For expression says "execute the following loop for every row returned in the cursor" but there are no rows in the cursor so it never executes the loop. Further the structure actually indicates you are expecting multiple for a given p_id. If that's not the case you can eliminate the cursor all together. Assuming p_id is the primary key you have either 0 or 1 row so:
create or replace function stock_info (p_id in number)
return text
is
lv_movie_info varchar2(100);
begin
select i.movie_title || ' is available: ' || i.movie_qty || ' on the shelf'
into lv_movie_info
from mm_movie i
where p_id = movie_id;
return lv_movie_info;
exceptions
when no_data_found
then return 'no data found';
end stock_info;
Of course if do expect more that 1 row the cursor is needed, but the IF is not as the were clause guarantees it's true. Still with 0 rows the loop will not be executed so the 'no data found' message needs to go after "End Loop".
Belayer
the cursor statement you used fetches data from the in parameter. i.e., in the cursor select you limiting based on the movie id passed.
on passing a movie id which is not in the data base, the cursor select statement would not fetch any records, and so the flow won't even go inside the for loop.
if you wanted to return no data found - on passing a movie id which is not in the database, two ways to resolve
1. before the loop, have select statement to set a flag to Y or N if exists according and to have your requirement.
2. in if not using for cursor, there is an option to check not found...
sample:
declare
cursor c1 is select * from table_sample; -- empty table
c_rec c1%rowtype;
begin
open c1;
fetch c1 into c_rec;
if c1%notfound then
dbms_output.put_line('not found');
end if;
close c1;
end;
-- output
not found

Multiple Records being fetched in Cursor PLSQL Procedure

Table : WSH_DEL_DETAILS_INTERFACE
Unique Column:DELIVERY_DETAIL_INTERFACE_ID
Input to procedure : DELIVERY_DETAIL_INTERFACE_ID
Column values to be fetched:SALES_ORDER_LINE_NUMBER , SALES_ORDER_NUMBER
Expected Output:
Single record
Actual Output:
All records in table are being fetched
Code:
create or replace PROCEDURE procedurevalidation(
delivery_detail_interface_id IN
WSH_DEL_DETAILS_INTERFACE.DELIVERY_DETAIL_INTERFACE_ID%TYPE,
ROW_COUNT OUT INTEGER)
IS
CURSOR wddi_cur IS SELECT * FROM WSH_DEL_DETAILS_INTERFACE WHERE
DELIVERY_DETAIL_INTERFACE_ID = delivery_detail_interface_id;
wddi_record WSH_DEL_DETAILS_INTERFACE%ROWTYPE;
BEGIN
OPEN wddi_cur;
LOOP
FETCH wddi_cur into wddi_record;
EXIT when wddi_cur%NOTFOUND;
DBMS_OUTPUT.ENABLE(100000);
DBMS_OUTPUT.PUT_LINE(delivery_detail_interface_id);
DBMS_OUTPUT.PUT_LINE('SALESORDERNUMBER111:::: ' ||
wddi_record.SALES_ORDER_NUMBER);
DBMS_OUTPUT.PUT_LINE('SALESORDERLINENUMBER1111::::: ' ||
wddi_record.SALES_ORDER_LINE_NUMBER);
DBMS_OUTPUT.PUT_LINE('COUNT' || ROW_COUNT);
END LOOP;
CLOSE wddi_cur;
end;
You need to change the names of the input variable to your procedure.
create or replace PROCEDURE procedurevalidation(
p_delivery_detail_interface_id IN
WSH_DEL_DETAILS_INTERFACE.DELIVERY_DETAIL_INTERFACE_ID%TYPE,
ROW_COUNT OUT INTEGER)
And in your cursor you need to change the variable name as well.
CURSOR wddi_cur
IS
SELECT *
FROM WSH_DEL_DETAILS_INTERFACE
WHERE DELIVERY_DETAIL_INTERFACE_ID = p_delivery_detail_interface_id;
Your cursor is returning all records in the table because you are equating the table's column itself and not matching it with the input variable in the procedure

Oracle cursor with variable columns/tables/criteria

I need to open a cursor while table name, columns and where clause are varying. The table name etc will be passed as parameter. For example
CURSOR batch_cur
IS
SELECT a.col_1, b.col_1
FROM table_1 a inner join table_2 b
ON a.col_2 = b.col_2
WHERE a.col_3 = 123
Here, projected columns, table names, join criteria and where clause will be passed as parameters. Once opened, i need to loop through and process each fetched record.
You need to use dynamic SQL something like this:
procedure dynamic_proc
( p_table_1 varchar2
, p_table_2 varchar2
, p_value number
)
is
batch_cur sys_refcursor;
begin
open batch_cur for
'select a.col_1, b.col_1
from ' || p_table_1 || ' a inner join || ' p_table_2 || ' b
on a.col_2 = b.col_2
where a.col_3 = :bind_value1';
using p_value;
-- Now fetch data from batch_cur...
end;
Note the use of a bind variable for the data value - very important if you will re-use this many times with different values.
From your question i guess you need a dynamic cursor. Oracle provides REFCURSOR for dynamic sql statements. Since your query will be built dynamically you need a refcursor to do that.
create procedure SP_REF_CHECK(v_col1 number,v_col2 date,v_tab1 number,v_var1 char,v_var2 varchar2)
is
Ref_cur is REF CURSOR;
My_cur Ref_cur;
My_type Table_name%rowtype;
stmt varchar2(500);
begin
stmt:='select :1,:2 from :3 where :4=:5';
open My_cur for stmt using v_col1,v_col2,v_tab1,v_var1,v_var2;
loop
fetch My_cur into My_type;
//do some processing //
exit when My_cur%notfound;
end loop;
close My_cur;
end;
Check this link for more http://docs.oracle.com/cd/B10500_01/appdev.920/a96624/11_dynam.htm

how inner cursor use a field of outer cursor in oracle?

I have two nested cursors. I mean one cursor Is Inside of another one.
I want to use a field of outer cursor inside the inner one. something like this:
Inner_cursor.outer_cursor.outer_cursor_column;
but It does not work even I use like this:
Inner_cursor.(outer_cursor.outer_cursor_column);
Is there any way I could do this?
EDIT:
This Is My Code:
CREATE OR REPLACE PROCEDURE TEST1
AS
CURSOR loop_relation IS
SELECT * FROM RELATION_table;
relation_rec loop_relation%rowtype;
CURSOR loop_BIG_TABLE IS
SELECT * FROM BIG_TABLE;
BIG_TABLE_rec loop_BIG_TABLE%rowtype;
BEGIN
FOR RELATION_REC IN LOOP_RELATION
LOOP
FOR BIG_TABLE_rec in loop_BIG_TABLE
LOOP
IF (BIG_TABLE_REC.RELATION_REC.DESTINATION_PK IS NULL) THEN
UPDATE BIG_TABLE
SET BIG_TABLE.RELATION_REC.DESTINATION_PK = (
SELECT RELATION_REC.SOURCE_FK FROM RELATION_REC.SOURCE_TABLE
WHERE RELATION_REC.SOURCE_PK = BIG_TABLE_REC.RELATION_REC.SOURCE_PK)
WHERE BIG_TABLE_REC.ID = BIG_TABLE.ID;
END IF;
END LOOP;
END LOOP;
END TEST1;
/
my problem is in the lines that i use three dot(.) to use a value of outer cursor in inner cursor.
Here's an example of two nested cursors and variables from the outer one used in the inner one. I hope it helps you.
BEGIN
FOR r_outer in (
select tab1.field1
from table1 tab1 )
LOOP
FOR r_inner in (
select tab2.field2
from table2 tab2
where tab2.field2 = r_outer.field1 )
LOOP
dbms_output.put_line(r_outer.field1);
dbms_output.put_line(r_inner.field2);
END LOOP;
END LOOP;
END;
For reference, I created a procedure to display how to use outer cursor value inside inner cursor value. I hope this resolves your query.
CREATE OR REPLACE PROCEDURE cur_inside_cur(my_cur OUT sys_refcursor)
AS
CURSOR roy_cur IS
SELECT name FROM avrajit;
roy_cur1 roy_cur%ROWTYPE;
BEGIN
OPEN roy_cur;
LOOP
FETCH roy_cur INTO roy_cur1;
EXIT WHEN roy_cur%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(roy_cur1.name);
OPEN my_cur FOR
SELECT department FROM avrajit
WHERE name=roy_cur1.name;
END LOOP;
END cur_inside_cur;
OUTPUT
var c refcursor;
begin
cur_inside_cur(:c);
end;
print c;

How to find number of rows in cursor

I would like to find the number of rows in a cursor. Is there a keyword that can help? Using COUNT, we have to write a query. Any help will be greatly appreciated.
The cursor_variable.%ROWCOUNT is the solution. But its value will be 0 if you check it after opening. You need to loop through all the records, to get the total row count. Example below:
DECLARE
cur sys_refcursor;
cur_rec YOUR_TABLE%rowtype;
BEGIN
OPEN cur FOR
SELECT * FROM YOUR_TABLE;
dbms_output.put_line(cur%rowcount);--returning 0
LOOP
FETCH cur INTO cur_rec;
EXIT WHEN cur%notfound;
dbms_output.put_line(cur%rowcount);--will return row number beginning with 1
dbms_output.put_line(cur_rec.SOME_COLUMN);
END LOOP;
dbms_output.put_line('Total Rows: ' || cur%rowcount);--here you will get total row count
END;
/
You must open the cursor and then fetch and count every row. Nothing else will work.
You can also use BULK COLLECT so that a LOOP is not needed,
DECLARE
CURSOR c
IS SELECT *
FROM employee;
TYPE emp_tab IS TABLE OF c%ROWTYPE INDEX BY BINARY_INTEGER;
v_emp_tab emp_tab;
BEGIN
OPEN c;
FETCH c BULK COLLECT INTO v_emp_tab;
DBMS_OUTPUT.PUT_LINE(v_emp_tab.COUNT);
CLOSE c;
END;
/
Edit: changed employee%ROWTYPE to c%ROWTYPE
You can use following simple single line code to print cursor count
dbms_output.put_line(TO_CHAR(cur%rowcount));
This should work for you
DECLARE
CURSOR get_data_ IS
SELECT *
FROM table_abc_
WHERE owner = user_; -- your query
counter_ NUMBER:= 0;
BEGIN
FOR data_ IN get_data_ LOOP
counter_ := counter_ + 1;
END LOOP;
dbms_output.put_line (counter_);
END;
DECLARE #STRVALUE NVARCHAR(MAX),
#CREATEDDATE DATETIME,
#STANTANCEVALUE NVARCHAR(MAX),
#COUNT INT=0,
#JOBCODE NVARCHAR(50)='JOB00123654',
#DATE DATETIME=GETDATE(),
#NAME NVARCHAR(50)='Ramkumar',
#JOBID INT;
CREATE TABLE #TempContentSplitValue (ITEMS NVARCHAR(200))
SELECT #JOBID = i.Id FROM JobHeader_TBL i WHERE Id=1201;
IF EXISTS (SELECT 1 FROM JobHeader_TBL WHERE Id=#JOBID)
BEGIN
SELECT #STRVALUE= Description from ContentTemplate_TBL where Id=1
INSERT INTO #TempContentSplitValue SELECT * FROM dbo.split(#STRVALUE, '_')
SET #STRVALUE=''
DECLARE db_contentcursor CURSOR FOR SELECT ITEMS FROM #TempContentSplitValue
OPEN db_contentcursor
FETCH NEXT FROM db_contentcursor
INTO #STANTANCEVALUE
WHILE (##FETCH_STATUS = 0)
BEGIN
SET #STRVALUE += #STANTANCEVALUE + 'JOB00123654'
SET #COUNT += 1
SELECT #COUNT
FETCH NEXT FROM db_contentcursor INTO #STANTANCEVALUE
END
CLOSE db_contentcursor
DEALLOCATE db_contentcursor
DROP TABLE #TempContentSplitValue
SELECT #STRVALUE
END
Here I am trying to count the total number of customers with age greater than 25. So store the result in the cursor first. Then count the size of the cursor inside the function or in the main begin itself.
DECLARE
cname customer24.cust_name%type;
count1 integer :=0;
CURSOR MORETHAN is
SELECT cust_name
FROM customer24
where age>25;
BEGIN
OPEN MORETHAN;
LOOP
FETCH MORETHAN into cname;
count1:=count1+1;
EXIT WHEN MORETHAN%notfound;
END LOOP;
-- dbms_output.put_line(count1);
dbms_output.put_line(MORETHAN%ROWCOUNT);
CLOSE MORETHAN;
END;
There is a possible work around that may be useful/needed because of the overhead of accessing a database server over a network (e.g., when using Ajax calls)
Consider this:
CURSOR c_data IS
SELECT per_first_name , null my_person_count
FROM person
UNION
SELECT null as per_first_name , count( distinct per_id ) as my_person_count
FROM person
order by my_person_count ;
The first row fetched has the count of records. One MUST add specific columns fetched (the use of the * does not work), and one can add additional filters.
Try this:
print(len(list(cursor)))
I always read that people loop through results. Why not using a count(*)?
An example from my production code:
PROCEDURE DeleteStuff___(paras_ IN Parameters_Type_Rec)
IS
CURSOR findEntries_ IS
select * from MyTable
where order_no = paras_.order_no;
counter_ NUMBER;
CURSOR findEntries_count_ IS
SELECT COUNT(*) from MyTable
where order_no = paras_.order_no;
BEGIN
OPEN findEntries_count_;
FETCH findEntries_count_ INTO counter_;
CLOSE findEntries_count_;
dbms_output.put_line('total records found: '||counter_);
IF (counter_ = 0) THEN
-- log and leave procedure
RETURN;
END IF;
FOR order_rec_ IN findEntries_ LOOP
EXIT WHEN findEntries_%NOTFOUND OR findEntries_%NOTFOUND IS NULL;
-- do stuff - i.e. delete a record.
API_Package.Delete(order_rec_);
END LOOP;
END DeleteStuff___;
If the query is small, that is my prefered way.
In this example, I just want to know (and log) how many entries I'll delete.
p.s. Ignore the three underlines. In IFS, this is used when you want private procedures or functions.
You can’t have cursor count at start. For that you need to fetch complete cursor; that is the way get cursor count.
declare
cursor c2 is select * from dept;
var c2%rowtype;
i number :=0;
begin
open c2;
loop
fetch c2 into var;
exit when c2%NOTFOUND;
i: = i+1;
end loop;
close c2;
dbms_output.put_line('total records in cursor'||i);
end;
You can use %ROWCOUNT attribute of a cursor.
e.g:
DECLARE
CURSOR lcCursor IS
SELECT *
FROM DUAL;
BEGIN
OPEN lcCursor ;
DBMS_OUTPUT.PUT_LINE(lcCursor%ROWCOUNT);
CLOSE lcCursor ;
END;

Resources