Oracle 11g - Add query results to SYS_REFCURSOR in a LOOP - oracle

Currently, I'm working on creating a stored procedure that I can pass a list of strings to from my C# application using a DataReader. So part of it is also properly declaring that Associative Array so the stored procedure can accept the data.
Here is what I currently have (I'm not too concerned about the C# side of things at the moment):
CREATE OR REPLACE PACKAGE TEST_PACKAGE_01
AS
TYPE t_string_list is table of VARCHAR2(4000) index by pls_integer;
PROCEDURE SP_TEST_01(in_list IN t_string_list, out_cursor OUT SYS_REFCURSOR);
END TEST_PACKAGE_01;
/
CREATE OR REPLACE PACKAGE BODY TEST_PACKAGE_01
AS
PROCEDURE SP_TEST_01(in_list IN t_string_list, out_cursor OUT SYS_REFCURSOR)
IS
BEGIN
OPEN out_cursor;
FORALL indx IN 1..in_list.COUNT
-- I have tried numerous different things here with no success, this is just what I have in my latest iteration
INSERT INTO out_cursor Values (SELECT * FROM SOME_TABLE WHERE SOME_COLUMN = in_list(indx));
CLOSE out_cursor;
END SP_TEST_01;
END TEST_PACKAGE_01;
Within the body of the procedure, I managed to get it to return a SYS_REFCURSOR just fine, albeit just a test without any input. Something kind of like:
OPEN out_cursor FOR
SELECT * FROM SOME_TABLE;
I can't use a simple IN statement for the query against SOME_TABLE since in_list can potentially contain thousands of records. Ideally, I'd like to populate out_cursor with one statement, instead of performing a loop.
Cursors are pretty new territory for me, so maybe it isn't possible to do what I'm thinking with them, but I haven't seen anything to the contrary.

In 11g you can't access the PL/SQL table type from SQL, even if that SQL is within a PL/SQL block. You can create a schema-level type instead:
CREATE OR REPLACE TYPE t_string_list AS TABLE OF VARCHAR2(4000)
/
Then your package becomes:
CREATE OR REPLACE PACKAGE TEST_PACKAGE_01
AS
PROCEDURE SP_TEST_01(in_list IN t_string_list, out_cursor OUT SYS_REFCURSOR);
END TEST_PACKAGE_01;
/
CREATE OR REPLACE PACKAGE BODY TEST_PACKAGE_01
AS
PROCEDURE SP_TEST_01(in_list IN t_string_list, out_cursor OUT SYS_REFCURSOR)
IS
BEGIN
OPEN out_cursor FOR
SELECT *
FROM SOME_TABLE
WHERE SOME_COLUMN IN (
SELECT * FROM TABLE(in_list)
);
END SP_TEST_01;
END TEST_PACKAGE_01;
/
or with a join instead of IN:
CREATE OR REPLACE PACKAGE BODY TEST_PACKAGE_01
AS
PROCEDURE SP_TEST_01(in_list IN t_string_list, out_cursor OUT SYS_REFCURSOR)
IS
BEGIN
OPEN out_cursor FOR
SELECT st.*
FROM TABLE(in_list) t
JOIN SOME_TABLE st ON st.SOME_COLUMN = t.COLUMN_VALUE;
END SP_TEST_01;
END TEST_PACKAGE_01;
/
The TABLE() is a table collection expression.
db<>fiddle with made-up table and anonymous block to test.

Related

Create record from refcursor

I would like to create record from refcursor. My code:
set serveroutput on
DECLARE
c_curs SYS_REFCURSOR;
v_id NUMBER;
BEGIN
pck_prov.get_value_type_list (1, c_curs); --> procedure called here
-- I guess this is the place where record can be created from cursor.
LOOP
FETCH c_curs
INTO v_id;--instead of fetching into variable I would like to fetch into row
EXIT WHEN c_curs%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(v_id);--if fetching is done into row, only selected columns can be printed, like myrow.id
END LOOP;
CLOSE c_curs;
END;
Please note: I know how to create record from cursor which is defined with select statement as it is described here. What I don't know is how to use same technique for refcursors.
EDIT:
Code from here is just what I need, but it throws error:
set serveroutput on
VAR c_curs refcursor;
EXECUTE pck_prov.get_value_type_list(1, :c_curs);
BEGIN
FOR record_test IN :c_curs LOOP
dbms_output.put_line(record_test.id);
END LOOP;
END;
Error: error PLS-00456: item 'SQLDEVBIND1Z_1' is not a cursor.
Just to clarify question:
In my database there is around 200 packages.
Every package has several stored procedures inside - and usually each procedure is combined with columns from different tables. That is why it would be the best to have some dynamically created cursor, so I can make simple select just like in the example I've posted.
From Oracle 12.1 onward, you could use the DBMS_SQL.return_result procedure. SQL Plus displays the contents of implicit statement results automatically. So, rather than defining explicit ref cursor out parameters, the RETURN_RESULT procedure in the DBMS_SQL package allows you to pass them out implicitly.
DECLARE
c_curs SYS_REFCURSOR;
v_id NUMBER;
BEGIN
pck_prov.get_value_type_list (1, c_curs);
DBMS_SQL.return_result(c_curs); --> Passes ref cursor output implicitly
END;
/
In fact, no need of this separate PL/SQL block, you could add the DBMS_SQL.return_result(c_curs) statement in your original pck_prov.get_value_type_list procedure itself.
Just define a PL/SQL RECORD type that matches the cursor and FETCH into it.
DECLARE
c_curs SYS_REFCURSOR;
TYPE rec_t IS RECORD ( object_name VARCHAR2(30), object_type VARCHAR2(30) );
v_rec rec_t;
BEGIN
OPEN c_curs FOR
SELECT object_name,
object_type
FROM dba_objects
WHERE object_name like 'DBA%TAB%';
LOOP
FETCH c_curs
INTO v_rec;
EXIT WHEN c_curs%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(v_rec.object_name || ' - ' || v_rec.object_type);
END LOOP;
CLOSE c_curs;
END;
DBA_ADVISOR_SQLA_TABLES - VIEW
DBA_ADVISOR_SQLA_TABVOL - VIEW
DBA_ADVISOR_SQLW_TABLES - VIEW
DBA_ADVISOR_SQLW_TABVOL - VIEW
DBA_ALL_TABLES - VIEW
etc...

PL SQL Procedure from Trigger

I created procedure GETDEL that select name from students , but when i try to call this procedure in trigger it says i gave wrong types of argument to call
I have been trying to pass (records) or (records OUT SYS_REFCURSOR) as a argument but it dont works
CREATE OR REPLACE PROCEDURE GETDEL(records OUT SYS_REFCURSOR) AS
BEGIN
OPEN records FOR
SELECT name FROM students;
END GETDEL;
CREATE OR REPLACE TRIGGER After_delete_student
AFTER DELETE ON TABLE2
FOR EACH ROW
DECLARE
rec sys_refcursor;
BEGIN
GETDELCZL();
END;
Your procedure contains a single parameter and, even though it's an out parameter, the calling procedure is responsible the defining it.
create or replace
trigger after_delete_student
after delete
on table2
for each row
declare
result_rec sys_refcursor;
begin
getdel(result_rec);
end;

Error(4,5): PLS-00428: an INTO clause is expected in this SELECT statement

create or replace PROCEDURE find_Doctor (p_SSN in number) AS
BEGIN
select drName, drPhone
from clients
where SSN = p_SSN;
END find_Doctor;
I've got this stored procedure and I just want to output the resulting table from that call. Is there an easy way to do this without declaring a temporary table? I can't just make is a normal SQL query because I have to call it from a java program.
In a Procedure you would need variable to hold the result output of the SQL query. You can then use the variable. Use this:
CREATE OR REPLACE PROCEDURE find_Doctor (p_SSN IN NUMBER)
AS
var_nm VARCHAR2 (100);
var_ph NUMBER;
BEGIN
SELECT drName, drPhone
INTO var_nm, var_ph
FROM clients
WHERE SSN = p_SSN;
DBMS_OUTPUT.put_line ('Doc Name - ' || var_nm || 'Doc Ph. No-' || var_ph);
END find_Doctor;
Edit:
I can't just make is a normal SQL query because I have to call it from
a java program.
You can then use SYS_REFCUSOR to return results, which can be mapped to a JDBC ResultSet.
CREATE OR REPLACE PROCEDURE find_Doctor (p_SSN IN NUMBER,
VAR OUT SYS_REFCURSOR)
AS
BEGIN
OPEN VAR FOR
SELECT drName, drPhone
FROM clients
WHERE SSN = p_SSN;
END find_Doctor;
You should define out parameters drName and drPhone:
create or replace PROCEDURE find_Doctor (p_SSN in number, p_drName OUT
VARCHAR2, p_drPhone OUT VARCHAR2) AS
BEGIN
select drName, drPhone
into p_drName, p_drPhone
from clients
where SSN = p_SSN;
END find_Doctor;
Easy way is to learn tool you're used and best practices for it.
For me, best way is remove senseless procedure and just selects data you need. But if you adherent of 'SP only' approach you can use ref cursor to retrive required data:
create or replace function find_Doctor (p_SSN in number)
return sys_refcursor as
v_result sys_refcursor;
BEGIN
open v_result for
select drName, drPhone
from clients
where SSN = p_SSN;
return v_result;
END find_Doctor;

To get data from Stored Procedure having without parameter

I have a stored procedure proc1 without parameters. I want to extract data from this stored procedure. How can I get that? Could you help me?
Stored procedure:
create procedure proc1
as
begin
select e_id, e_nm, e_sal
from emp
where e_id like 'e%';
end proc1;
You can do this in Oracle 12.1 or above:
create or replace procedure demo
as
rc sys_refcursor;
begin
open rc for select * from dual;
dbms_sql.return_result(rc);
end demo;
This requires an Oracle 12.1 or later client/driver to handle the implicit result set.
For more details, see Implicit Result Sets in the Oracle 12.1 New Features Guide, Tom Kyte's Blog, Oracle Base etc.
Here's one possible solution:
Declaration:
create procedure proc1 (emp_row IN OUT emp%rowtype)
as
begin
select * --e_id, e_nm, e_sal
into emp_row
from emp
where e_id like 'e%';
end proc1;
Use case:
DECLARE
l_emp_row emp%rowtype;
BEGIN
proc1(l_emp_row);
-- Here you can access every column of table "emp", like so:
-- dbms_output.put_line('e_id: ' || to_char(l_emp_row.e_id));
-- dbms_output.put_line('e_nm: ' || to_char(l_emp_row.e_nm));
-- dbms_output.put_line('e_sal: ' || to_char(l_emp_row.e_sal));
END;
Is there anything special that you need to get out of the Procedure?
Cheers
you create a view this query. (recomended)
Oracle procedure is not return any data. So, you do not see any result. Your result get buffer but not print screen. if You need a procedure, insert all data another table.
create procedure proc1
as
begin
insert into new_table select e_id, e_nm, e_sal from emp where e_id like 'e%';
end proc1;
Another way; You create a function. Because function outputs and inputs. This function;
for example Create an Oracle function that returns a table

Oracle PL/SQL Developer: Return %RowType from Package Procedure

i'm kind of new to Oracle Pl\SQL. I was just trying to create a simple Package with a procedure that returns a set of object id's; the code is as follows:
--Package Spec
CREATE OR REPLACE PACKAGE TEST IS
--GET OBJECT ID'S FROM CONTROL TABLE
PROCEDURE get_object_id_control(p_obj_id OUT abc_table%ROWTYPE);
END;
--Package Body
PROCEDURE get_object_id_control(p_obj_id OUT abc_table%ROWTYPE) AS
BEGIN
SELECT object_id
INTO p_obj_id
FROM abc_table
WHERE fec_proc IS NULL;
END;
I get Error: PL/SQL: ORA-00913: too many values. Is this the correct way for returning multiple values of same data type, or is there a better approach. Thanks in advance.
You can create a custom table type and set the out parameter of the procedure to that type.
CREATE TABLE ABC_TABLE(ID varchar2(100));
create or replace type abc_tab is table of varchar2(100);
/
CREATE OR REPLACE PACKAGE TEST IS
PROCEDURE get_object_id_control(p_obj_id OUT abc_tab);
END;
/
CREATE OR REPLACE PACKAGE BODY TEST IS
PROCEDURE get_object_id_control(p_obj_id OUT abc_tab) AS
BEGIN
SELECT id
bulk collect INTO p_obj_id
FROM abc_table;
END;
END;
/
Then you can call it like so:
declare
v abc_tab;
begin
TEST.get_object_id_control(p_obj_id => v);
for i in v.first..v.last loop
dbms_output.put_line(v(i));
end loop;
end;
/
Similar to GurV's answer (since he beat me by like 30 seconds...), you can use a PL/SQL object type as well. You do not need the CREATE TYPE statement if you don't need to reference the type in SQL.
--Package Spec
CREATE OR REPLACE PACKAGE TEST AS
TYPE id_table_type IS TABLE OF NUMBER;
--GET OBJECT ID'S FROM CONTROL TABLE
PROCEDURE get_object_id_control(p_obj_id_list OUT id_table_type);
END;
--Package Body
CREATE OR REPLACE PACKAGE BODY TEST AS
PROCEDURE get_object_id_control(p_obj_id_list OUT id_table_type) AS
BEGIN
SELECT object_id
BULK COLLECT INTO p_obj_id_list
FROM abc_table
WHERE fec_proc IS NULL;
END;
END;
To use it:
DECLARE
l_id_list test.id_table_type;
BEGIN
test.get_object_id_control (p_obj_id_list => l_id_list);
FOR i IN l_id_list.FIRST .. l_id_list.LAST LOOP
DBMS_OUTPUT.put_line (l_id_list (i));
END LOOP;
END;

Resources