how to call plsql procedure out parameter as a user-defined ref cursor? - oracle

I have a package P and a precedure A in it.
create or replace package pkg_get_users_info_by_role
as
type user_info_ref_cur is ref cursor;
procedure get_user_info_proc
(p_role_name varchar2, p_user_info out user_info_ref_cur);
end pkg_get_users_info_by_role;
/
and the body;
create or replace package body pkg_get_users_info_by_role
as
procedure get_user_info_proc
(p_role_name varchar2, p_user_info out user_info_ref_cur)
as
begin
open p_user_info for
select user_id,username,user_password,role_name from user_info,role_info
where user_info.user_role=role_info.role_id
and role_info.role_name like p_role_name;
end;
end pkg_get_users_info_by_role;
My question is, how can I call the procedure? Do I need a variable of pkg_get_users_info_by_role.user_info_ref_cur type to call it? I cannot create a variable of this type. Is there any way to solve this?
Thanks!!

Yes, you simply define your cursor in your calling program as the package.type. Here's a simple-minded illustration that should work on most any oracle database:
CREATE OR REPLACE PACKAGE pkg AS
TYPE rc IS REF CURSOR;
PROCEDURE get_rc(p_rc OUT rc);
END pkg;
/
CREATE OR REPLACE PACKAGE BODY pkg AS
PROCEDURE get_rc(p_rc OUT rc) IS
BEGIN
OPEN p_rc FOR
SELECT t.owner, t.table_name FROM all_tables t;
END get_rc;
END pkg;
/
DECLARE
crsr pkg.rc;
v1 VARCHAR2(32);
v2 VARCHAR2(32);
BEGIN
pkg.get_rc(crsr);
LOOP
EXIT WHEN crsr%NOTFOUND;
FETCH crsr INTO v1, v2;
dbms_output.put_line(v1||': '||v2);
END LOOP;
CLOSE crsr;
END;
/

Related

PLS-00306: wrong number or types of arguments in call to procedure PROC_T

declare
TYPE stag_tab IS TABLE OF d_staging%ROWTYPE;
stag_tab1 stag_tab;
begin
--Bulk Collect
select * bulk collect into staging_tab1 from d_staging;
PKG_T.PROC_T(stag_tab1);
end;
/
Package definition:
--Package
CREATE OR REPLACE PACKAGE PKG_T
AS
TYPE staging IS TABLE OF d_staging%ROWTYPE;
PROCEDURE PROC_T(p_staging IN staging);
END PKG_T;
/
-- Package Body
CREATE OR REPLACE PACKAGE BODY PKG_T
AS
PROCEDURE PROC_T (p_staging IN staging)
AS
VAR1 d_staging%ROWTYPE;
CUR1 SYS_REFCURSOR;
QUERY_STRING VARCHAR2(2000);
BEGIN
OPEN CUR1 FOR SELECT * from table(p_staging);
LOOP
FETCH CUR1 into VAR1;
EXIT WHEN cur1%NOTFOUND;
INSERT into d (testdata) VALUES (var1.testval1);
COMMIT;
END LOOP;
END;
END PKG_T;
/
You are receiving the error because the procedure PKG_T.PROC_T is expecting a parameter of type staging, but when you are calling the procedure you are passing it a variable of type stag_tab. The type of the variable being passed to the procedure needs to match the type of the parameter definition for the procedure.
Your procedure declaration:
PROCEDURE PROC_T (p_staging IN staging)
Takes the argument as type staging.
You are passing the argument as a locally defined type:
TYPE stag_tab IS TABLE OF d_staging%ROWTYPE;
These are different types. Instead, you need the PL/SQL block to be:
declare
stag_tab1 package_name.staging;
begin
select *
bulk collect into stag_tab1
from d_staging;
PKG_T.PROC_T(stag_tab1);
end;
/

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;

Join between a collection and a table

I have a problem with procedures in PL/SQL. I have a public procedure declared in a package and I want to call another procedure(private) inside the first one.
PROCEDURE show_notesforstudent (number_id IN number) as
...
DBMS_OUTPUT.PUT_LINE('The notes for the student with number_id X are: '|| Y);
DBMS_OUTPUT.PUT_LINE('here is the call for the private procedure');
erase_student(number_id);
END;
This code is a general example, my code is bigger and I can't put it all here. Here is only the main idea.
For this call I face this error: "Error(31,5): PLS-00313: 'erase_student' not declared in this scope".
The implementation of erase_student procedure is:
PROCEDURE erase_student(n_id students.number_id%type) AS
student_inexistent EXCEPTION;
PRAGMA EXCEPTION_INIT(student_inexistent, -20002);
counter integer;
BEGIN
SELECT COUNT(nmber_id) INTO counter FROM studens where number_id = n_id;
IF counter = 0 THEN
raise student_inexistent;
END IF;
DELETE FROM students WHERE number_id = n_id;
EXCEPTION
WHEN student_inexistent THEN
raise_application_error (-20002, 'Student with number_id' || n_id || ' doesn't exists in database');
END stergere_student;`
Lets simplify your code and make it syntactically correct:
CREATE OR REPLACE PACKAGE package_name
AS
PROCEDURE show_notesforstudent (number_id IN number);
END;
/
CREATE OR REPLACE PACKAGE BODY package_name
AS
PROCEDURE show_notesforstudent (number_id IN number)
AS
BEGIN
erase_student(number_id);
END;
PROCEDURE erase_student(n_id NUMBER) AS BEGIN NULL; END;
END;
/
However, it still won't compile as we get PLS-00313: 'ERASE_STUDENT' not declared in this scope. It can be fixed by re-organising the package to ensure that the private procedure is declared before it is called; like this:
CREATE OR REPLACE PACKAGE BODY package_name
AS
PROCEDURE erase_student(n_id NUMBER) AS BEGIN NULL; END; -- Header and body
PROCEDURE show_notesforstudent (number_id IN number)
AS
BEGIN
erase_student(number_id);
END;
END;
/
However, if you want to keep the order of the procedures in the package then you could just use a forward declaration to declare the header for the private procedure before it is called and declare the body afterwards; like this:
CREATE OR REPLACE PACKAGE BODY package_name
AS
PROCEDURE erase_student(n_id NUMBER); -- Header only
PROCEDURE show_notesforstudent (number_id IN number)
AS
BEGIN
erase_student(number_id);
END;
PROCEDURE erase_student(n_id NUMBER) AS BEGIN NULL; END; -- Header and body
END;
/

Failing to create and execute PL/SQL procedure from SQL*Plus

create or replace package XXX_MWA_LOV_TEST AS
TYPE t_ref_csr IS REF CURSOR;
PROCEDURE XXX_USERS_LOV (p_user_name IN VARCHAR2,x_users OUT NOCOPY t_ref_csr);
end XXX_MWA_LOV_TEST;
How to run XXX_USERS_LOV procedure in SQL*Plus?
I tried this code but getting the syntax error:
SQL> EXECUTE XXX_MWA_LOV_TEST.XXX_USERS_LOV('%',:my_p_out);
Procedure body:
create or replace package body XXX_MWA_LOV_TEST AS
PROCEDURE XXX_USERS_LOV (x_users OUT NOCOPY t_ref_csr,p_user_name IN VARCHAR2) IS
BEGIN
OPEN x_users FOR select user_id,user_name,description
from fnd_user
where user_name like p_user_name;
DBMS_OUTPUT.PUT_LINE('TESTING DATA');
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('ERROR IN USER LOV '|| SQLERRM);
END XXX_USERS_LOV;
end XXX_MWA_LOV_TEST;
First of all, the order of the parameters must be the same in the spec and in the package body.
You can call the ref cursor like this:
declare
v_cursor xxx_mwa_lov_test.t_ref_csr;
var1 the_type_you_need;
var2 the_type_you_need;
...
begin
xxx_mwa_lov_test.xxx_users_lov(v_cursor,'username...');
loop
fetch v_cursor into var1, var2...;
exit when v_cursor%notfound;
... do what you want with the vars...
end loop;
end;
/

Variable from an procedure to the parameter of another procedure

Using a package, how can i pass a variable assign in a procedure to the parameter of another procedure?
I'm not sure if you are wondering about global variables or how OUT parameters work. An example of using a procedure's OUT parameter is below. More info on using IN and OUT can be found here
create or replace package TEST_PKG
IS
procedure test(p_input IN NUMBER, p_output OUT NUMBER)
IS
BEGIN
p_output := p_input * p_input;
END test ;
procedure testRun
IS
v_input NUMBER := 0;
v_output NUMBER;
BEGIN
test(v_input, v_output);
DBMS_OUTPUT.PUT_LINE('OUTPUT : ' || v_output );
END test ;
END xyz;
create or replace package xyz
IS
procedure test
IS
v_temp varchar2(20); --local variable
BEGIN
v_temp:=20; --assigning value to a variable
--if procedure is within the same package,as this example shows ,then call as below
test2(v_temp);
--if procedure is in another package say PQR then call as below,
--but the procedure should be declared in the specification of the package PQR
PQR.test2(v_temp);
--if procedure is not inside any package ,just a single procedure is there
--,then call as below
test2(v_temp);
END test ;
procedure test2(p_temp IN varchar2)
IS
BEGIN
--do you processing
END test2;
END xyz;
Hope this helps

Resources