PL/SQL - Trying to create a simple procedure - oracle

EDITED :
I am trying to create a procedure where a Ref Cursor is defined as an OUT parameter, my question is how do I call out that Ref Cursor when I execute it for example here is how I want to call it in my EXEC sql command :
EXEC film_not_in_stock(2,2,vcur);
Here is the procedure :
CREATE OR REPLACE PROCEDURE film_not_in_stock( p_film_id IN NUMBER, p_store_id IN NUMBER, vcur OUT SYS_REFCURSOR)
IS
cur sys_refcursor;
v_cur inventory.inventory_id%TYPE --or the same type the inventory_id column is
v_cur sys_refcursor;
BEGIN
OPEN cur FOR SELECT inventory_id
FROM inventory
WHERE film_id = p_film_id
AND store_id = p_store_id
AND inventory_id NOT IN (SELECT inventory_in_stock(inventory_id) FROM dual);
fetch cur into v_cur;
EXIT WHEN cur%NOTFOUND;
END;
/
I know there are typos in procedure as well, will appreciate if you can help me put it right.
Thank you very very much !!
Tonya

Try as
variable p_cursor REFCURSOR;
DECLARE
BEGIN
film_not_in_stock(2,2,:p_cursor);
end;
/
print p_cursor;
And modify procedure as
CREATE OR REPLACE PROCEDURE film_not_in_stock (
p_film_id IN inventory.film_id%TYPE,
p_store_id IN inventory.store_id%TYPE,
vcur OUT SYS_REFCURSOR)
AS
BEGIN
OPEN vcur FOR
SELECT inventory_id
FROM inventory
WHERE film_id = p_film_id
AND store_id = p_store_id
AND inventory_id NOT IN
(SELECT inventory_in_stock (inventory_id) FROM DUAL);
END;
/

The following will do :
var results refcursor
exec film_not_in_stock(2,2,:results);

You need to declare a variable to fetch your cursor results into...
Something like this...
CREATE OR REPLACE PROCEDURE film_not_in_stock( p_film_id IN NUMBER, p_store_id IN NUMBER, p_film_count OUT NUMBER, cur OUT SYS_REFCURSOR)
IS
cur sys_refcursor;
v_cur inventory.inventory_id%TYPE --or the same type the inventory_id column is
BEGIN
OPEN cur FOR SELECT inventory_id
FROM inventory
WHERE film_id = p_film_id
AND store_id = p_store_id
AND inventory_id NOT IN (SELECT inventory_in_stock(inventory_id) FROM dual);
--SELECT FOUND_ROWS() INTO p_film_count FROM dual; <<----- **This is the line**
fetch cur into v_cur;
EXIT WHEN %NOTFOUND;
--do whatever you want
END;
/

Related

How to open a sys_refcursor with values from a normal cursor?

Can I open a sys_refcursor with value from a normal cursor?
create or replace procedure test(C1 out sys_refcursor)
Lv_c1 as
Select * from table;
Begin
Open C1 for select * from lv_c1;
End;
No, you cannot. "Normal" cursor is a PL/SQL variable, so it cannot be used in SQL query.
But it's possible to open a cursor for a result set of a cursor variable:
create or replace package pack as
cursor cur is
select rownum attr_1 from dual connect by level<=3;
type rset is table of cur%rowtype;
procedure getCursor (rc out sys_refcursor);
end;
/
create or replace package body pack as
procedure getCursor (rc out sys_refcursor) is
rs rset;
begin
open cur;
fetch cur bulk collect into rs;
close cur;
open rc for select * from table (rs);
end;
end;
/
Execution and the result:
var rc refcursor
exec pack.getCursor (:rc)
ATTR_1
--------
row1
row2
row3

Create function returns sys_refcursor

How to create a function to display all employees of one department?
I'd tried this code but it returns only "cursor" value.
CREATE OR REPLACE FUNCTION emp_dept (dept_id IN NUMBER)
RETURN SYS_REFCURSOR
IS
emp_name SYS_REFCURSOR;
BEGIN
OPEN emp_name
FOR SELECT last_name
FROM employees
WHERE department_id = dept_id;
RETURN emp_name;
END emp_dept;
You may use these options to read and display output from the cursor that's returned from your function.
Use a simple select
select emp_dept(10) from dual;
Result
EMP_DEPT(10)
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
LAST_NAME
-------------------------
Whalen
Use DBMS_SQL.RETURN_RESULT ( Oracle 12c and above)
DECLARE
l_cur SYS_REFCURSOR := emp_dept(10) ;
BEGIN
DBMS_SQL.RETURN_RESULt(l_cur);
END;
/
Result
PL/SQL procedure successfully completed.
ResultSet #1
LAST_NAME
-------------------------
Whalen
FETCH from the cursor into a local collection. A slight variation could be also be used to fetch into a scalar variable.
DECLARE
l_cur SYS_REFCURSOR := emp_dept(10) ;
TYPE l_last_name_tab IS TABLE OF employees.last_name%TYPE;
l_lnt l_last_name_tab;
BEGIN
FETCH l_cur BULK COLLECT INTO l_lnt;
for i in 1..l_lnt.count
loop
dbms_output.put_line(l_lnt(i));
end loop;
END;
/
Result
Whalen
PL/SQL procedure successfully completed.

How to fetch a ref_cursor into Oracle's WITH CLAUSE

Is it possible to use a ref_cursor with ORACLE WITH CLAUSE. For example, I have the following scenario. First I have a procedure which returns a ref_cursor
PROCEDURE p_return_cursor(p_id IN NUMBER, io_cursor OUT t_cursor)
AS
BEGIN
OPEN io_cursor FOR
SELECT col1, col2
FROM Table1 t
WHERE t.id = p_id;
END;
Second, I have another procedure on which I make a call to p_return_cursor:
PROCEDURE p_test(p_cid IN NUMBER)
AS
l_cursor t_cursor;
l_rec Table1%ROWTYPE;
BEGIN
p_return_cursor(p_id => p_cid, io_cursor => l_cursor);
-- CODE GOES HERE
...
Now, my question is, can I make a temp table using the Oracle's WITH CLAUSE using the cursor; something like:
...
WITH data AS (
LOOP
FETCH l_cursor INTO l_rec;
EXIT WHEN l_cursor%NOTFOUND;
SELECT l_rec.col1, l_rec.col2 FROM DUAL;
END LOOP;
CLOSE l_cursor;
)
You cannot do it directly. You can, however, BULK COLLECT your cursor into a PL/SQL table variable and use that in a WITH clause.
Be careful of memory usage if the cursor contains many rows.
Full example:
CREATE TABLE table1 ( col1 NUMBER, col2 NUMBER );
INSERT INTO table1 ( col1, col2 ) SELECT rownum, 100+rownum FROM DUAL CONNECT BY ROWNUM <= 15;
COMMIT;
CREATE OR REPLACE PACKAGE se_example AS
TYPE t_cursor IS REF CURSOR
RETURN table1%ROWTYPE;
TYPE l_rec_tab IS TABLE OF table1%ROWTYPE;
PROCEDURE p_test (p_cid IN NUMBER);
END se_example;
CREATE OR REPLACE PACKAGE BODY se_example AS
-- private
PROCEDURE p_return_cursor (p_id IN NUMBER, io_cursor OUT t_cursor) AS
BEGIN
OPEN io_cursor FOR
SELECT col1,
col2
FROM table1 t;
--WHERE t.id = p_id; -- I didn't put "id" column in my sample table, sorry...
END p_return_cursor;
PROCEDURE p_test (p_cid IN NUMBER) IS
l_cursor t_cursor;
l_tab l_rec_tab;
l_dummy NUMBER;
BEGIN
p_return_cursor (p_id => p_cid, io_cursor => l_cursor);
FETCH l_cursor BULK COLLECT INTO l_tab;
-- *** instead of this
-- WITH data AS (
-- LOOP
-- FETCH l_cursor INTO l_rec;
-- EXIT WHEN l_cursor%NOTFOUND;
-- SELECT l_rec.col1, l_rec.col2 FROM DUAL;
-- END LOOP;
-- CLOSE l_cursor;
-- )
-- '
--
-- *** do this
WITH data AS
( SELECT col1, col2 FROM TABLE(l_tab) )
SELECT sum(col1 * col2) INTO l_dummy
FROM data;
dbms_output.put_line('result is ' || l_dummy);
END p_test;
END se_example;
begin
se_example.p_test(100);
end;

How can I return dataset in procedure?

I have a procedure like this:
CREATE OR REPLACE PROCEDURE RIC.TEST
(P_VAR1 IN VARCHAR2, P_VAR2 IN VARCHAR2, P_VAR3 IN VARCHAR2, P_VAR4 IN VARCHAR2)
IS
L_RC SYS_REFCURSOR;
BEGIN
INSERT INTO RIC.TEMP_TABLE
SELECT * FROM RIC.TABLE WHERE COL1=P_VAR1 AND COL2=P_VAR2 AND COL3=P_VAR3 AND COL4=P_VAR4 AND ...;
OPEN L_RC
FOR 'SELECT .... FROM RIC.TEMP_TABLE WHERE ...';
...
TEMP_TABLE is a temporary table. I need to return and show second select output (SELECT .... FROM RIC.TEMP_TABLE WHERE ...) when I run this procedure. How can I do that? Is cursor wrong way for this?
Thank you.
Modify the procedure to
CREATE OR REPLACE PROCEDURE ric.test (p_var1 IN VARCHAR2,
p_var2 IN VARCHAR2,
p_var3 IN VARCHAR2,
p_var4 IN VARCHAR2,
p_cursor OUT SYS_REFCURSOR)
IS
BEGIN
INSERT INTO ric.temp_table
SELECT *
FROM ric.table
WHERE col1 = p_var1 AND col2 = p_var2 AND col3 = p_var3;
OPEN p_cursor FOR SELECT * FROM ric.temp_table;
END;
/
and execute the procedure as
VARIABLE cur REFCURSOR;
EXEC test (:cur);
PRINT cur;
If you would like to execute the procedure using SQL, wrap the procedure in a function which should be followed by calling the function in a SQL statement.
Example

Trying to understand how pl sql stored proc work and why my results differ

I have a simple stored proc...
create or replace
PROCEDURE GET_PERSON (aName VARCHAR2, p_data OUT sys_refcursor)
IS
BEGIN
OPEN p_data FOR SELECT * FROM people_table WHERE firstname = aName;
END;
However when I execute the stored proc it returns all of the records.
DECLARE
v_cur SYS_REFCURSOR;
v_1 number;
v_2 VARCHAR2(50);
v_3 VARCHAR2(200);
v_4 VARCHAR2(50);
v_5 VARCHAR2(50);
v_6 VARCHAR2(50);
BEGIN
GET_PERSON ('aaa#bbb.com', v_cur);
LOOP
FETCH v_cur INTO v_1, v_2, v_3, v_4, v_5, v_6;
EXIT WHEN v_cur%NOTFOUND;
dbms_output.put_line(v_2 || ' ' || v_3);
END LOOP;
CLOSE v_cur;
END;
If I run the simple statement
SELECT * FROM people_table WHERE firstname = 'aaa#bbb.com';
It correctly returns one record.
Why is the stored proc not behaving the same?
I found the issue..
My issue was name collision. When I altered the code above is when I noticed the issue. I originally has WHERE fistname = firstName. Once I changed the parameter to p_firstName as was well.

Resources