How to create an oracle stored procedure with an array parameter - oracle

I créated an Oracle stored procedure into a package. I did like this:
CREATE OR REPLACE PACKAGE PACKFACE IS
TYPE LIST_IDS IS TABLE OF INT INDEX BY BINARY_INTEGER;
PROCEDURE P_SELECT_IDBFRIENDS (CONSULTA OUT SYS_REFCURSOR,COD_US IN INT,IDS_NOT IN LIST_IDS);
END;
And the body of the package is:
CREATE OR REPLACE PACKAGE BODY PACKFACE IS
PROCEDURE P_SELECT_IDBFRIENDS (CONSULTA OUT SYS_REFCURSOR,COD_US IN INT, IDS_NOT IN LIST_IDS) IS
BEGIN
OPEN CONSULTA FOR
SELECT ID_US1,ID_US2 FROM T_FRIENDSHIP WHERE ID_US1=COD_US AND ID_US2 NOT IN (SELECT COLUMN_VALUE FROM TABLE(IDS_NOT));
END;
END;
/
These is ok in my Oracle 12c server, but I did the same code in Oracle 11g appeared an error, cannot access rows from a non-nested table ítem. What would be the solution? Thanks in advance
After this problem was fixed. Appears other one my Python code was broken. I have this procedure:
def select_ids(self,cod_us,ids_not):
lista = []
try:
cursor = self.__cursor.var(cx_Oracle.CURSOR)
varray = self.__cursor.arrayvar(cx_Oracle.NUMBER,ids_not)
l_query = self.__cursor.callproc("PROC_SELECT_IDS_ENT_AMISTADES", [cursor, cod_us, varray])
lista = l_query[0]
return lista
except cx_Oracle.DatabaseError as ex:
error, = ex.args
print(error.message)
return lista
PLS-00306 wrong number or type of arguments in call to a procedure. It was ok using Oracle 12c. Thanks in advance again.

You cannot use a collection type defined in PL/SQL in an SQL query in Oracle 11.
If you want to use a collection in both SQL and PL/SQL then you will have to define it in SQL:
CREATE TYPE LIST_IDS IS TABLE OF INT;
Then you can do:
CREATE OR REPLACE PACKAGE PACKFACE IS
PROCEDURE P_SELECT_IDBFRIENDS (
CONSULTA OUT SYS_REFCURSOR,
COD_US IN INT,
IDS_NOT IN LIST_IDS
);
END;
/
SHOW ERRORS;
CREATE OR REPLACE PACKAGE BODY PACKFACE IS
PROCEDURE P_SELECT_IDBFRIENDS (
CONSULTA OUT SYS_REFCURSOR,
COD_US IN INT,
IDS_NOT IN LIST_IDS
)
IS
BEGIN
OPEN CONSULTA FOR
SELECT ID_US1,ID_US2
FROM T_FRIENDSHIP
WHERE ID_US1=COD_US
AND ID_US2 NOT MEMBER OF IDS_NOT;
END;
END;
/
SHOW ERRORS;

First of all you should to create an oracle type like this:
CREATE OR REPLACE TYPE LIST_IDS AS TABLE OF INT;
And after that you should to create the package with the procedure and a nested table in parameter like this:
CREATE OR REPLACE PACKAGE PACKFACE IS
TYPE LISTADO_IDS IS TABLE OF INT INDEX BY PLS_INTEGER;
PROCEDURE P_SELECT_IDBFRIENDS (CONSULTA OUT SYS_REFCURSOR,COD_US IN INT,IDS_NOT IN LISTADO_IDS);
END;
Finally you should create the body. You pass the data to nested table from oracle type like this:
CREATE OR REPLACE PACKAGE BODY PACKFACE IS
PROCEDURE P_SELECT_IDBFRIENDS (CONSULTA OUT SYS_REFCURSOR,COD_US IN INT, IDS_NOT IN LISTADO_IDS)
IS
num_array FACEBOOK.LIST_IDS;
BEGIN
num_array:=LIST_IDS();
for i in 1 .. IDS_NOT.count
loop
num_array.extend(1);
num_array(i) := IDS_NOT(i);
end loop;
OPEN CONSULTA FOR
SELECT ID_US1,ID_US2 FROM T_FRIENDSHIP WHERE ID_US1=COD_US AND ID_US2 NOT IN (SELECT COLUMN_VALUE FROM TABLE(num_array));
END;
END;
I did that and I got the solution to the python error wrong number or type parameters. Good luck.

Related

PL/SQL Package Cursor with custom return value (like in the procedure)

Is it possible to define a cursor with custom return value (columns from different tables..) ?
For example:
CREATE OR REPLACE PACKAGE pkg
IS
CURSOR emp_cur (id_in IN employees.employee_id%TYPE)
RETURN employees%ROWTYPE;
END;
/
CREATE OR REPLACE PACKAGE BODY pkg
IS
CURSOR emp_cur (id_in IN employees.employee_id%TYPE)
RETURN employees%ROWTYPE
IS
SELECT *
FROM employees
WHERE employee_id = id_in;
END;
/
This works because the employees is a single table.
In the procedure i can define cursor
CURSOR CUR_TEST ....
and the later
TYPE t_cur_test IS TABLE OF CUR_TEST %ROWTYPE INDEX BY BINARY_INTEGER;
var_t_cur_test t_cur_test ;
an it works.
In the package however with
CREATE OR REPLACE PACKAGE TEST
IS
-- Public types
CURSOR CUR_TEST
RETURN CUR_TEST%ROWTYPE;
i am getting -> 1/14 PLS-00304: cannot compile body of 'CUR_TEST' without its specification.
Is there any way to do this ?

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;

pass pl/sql record as arguement to procedure

How to pass pl/sql record type to a procedure :
CREATE OR REPLACE PACKAGE BODY PKGDeleteNumber
AS
PROCEDURE deleteNumber (
list_of_numbers IN List_Numbers
)
IS
i_write VARCHAR2(5);
BEGIN
--do something
END deleteNumber;
END PKGDeleteNumber;
/
In this procedure deleteNumber I have used List_Numbers, which is a record type. The package declaration for the same is :
CREATE OR REPLACE PACKAGE PKGDeleteNumber
AS
TYPE List_Numbers IS RECORD (
IID NUMBER
);
TYPE list_of_numbers IS TABLE OF List_Numbers;
PROCEDURE deleteNumber (
list_of_numbers IN List_Numbers
);
END PKGDeleteNumber;
I have to execute the procedure deleteNumber passing a list of values. I inserted numbers in temp_test table, then using a cursor U fetched the data from it :
SELECT *
BULK COLLECT INTO test1
FROM temp_test;
Now, to call the procedure I am using
execute immediate 'begin PKGDELETENUMBER.DELETENUMBER(:1); end;'
using test1;
I have tried many other things as well(for loop, dbms_binding, etc). How do I pass a pl/sql record type as argument to the procedure?
EDIT:
Basically, I want to pass a list of numbers, using native dynamic sql only...
adding the table temp_test defn (no index or constraint):
create table test_temp (
IID number
);
and then inserted 1,2,3,4,5 using normal insert statements.
For this solution,
In a package testproc
CREATE TYPE num_tab_t IS TABLE OF NUMBER;
CREATE OR REPLACE PROCEDURE my_dyn_proc_test (p_num_array IN num_tab_t) AS
BEGIN
dbms_output.put_line(p_num_array.COUNT);
END;
/
this is called from sql prompt/toad
DECLARE
v_tab testproc.num_tab_t := testproc.num_tab_t(1, 10);
BEGIN
EXECUTE IMMEDIATE 'BEGIN testproc.my_dyn_proc_test(:1); END;' USING v_tab;
END;
this will not work.This shows error.I am not at my workstation so am not able to reproduce the issue now.
You can't use RECORD types in USING clause of EXECUTE IMMEDIATE statement. If you just want to pass a list of numbers, why don't you just use a variable of TABLE OF NUMBER type? Check below example:
CREATE TYPE num_tab_t IS TABLE OF NUMBER;
CREATE OR REPLACE PROCEDURE my_dyn_proc_test (p_num_array IN num_tab_t) AS
BEGIN
dbms_output.put_line(p_num_array.COUNT);
END;
/
DECLARE
v_tab num_tab_t := num_tab_t(1, 10);
BEGIN
EXECUTE IMMEDIATE 'BEGIN my_dyn_proc_test(:1); END;' USING v_tab;
END;
Output:
2
Edit
Try this:
CREATE TYPE num_tab_t IS TABLE OF NUMBER;
CREATE OR REPLACE PACKAGE testproc AS
PROCEDURE my_dyn_proc_test (p_num_array IN num_tab_t);
END;
/
CREATE OR REPLACE PACKAGE BODY testproc AS
PROCEDURE my_dyn_proc_test (p_num_array IN num_tab_t) AS
BEGIN
dbms_output.put_line(p_num_array.COUNT);
END;
END;
/
DECLARE
v_tab num_tab_t := num_tab_t(1, 10);
BEGIN
EXECUTE IMMEDIATE 'BEGIN testproc.my_dyn_proc_test(:1); END;' USING v_tab;
END;
Use an object type. object types are visible to all packages

Passing values from Oracle Object type parameter to PLSQL Table type parameter

How can we pass a parameter that is declared as an Oracle Object type to a Procedure having a parameter as PLSQL Table type?
Eg:
Procedure A(p_obj_emp t_obj_emp)
Procedure B(p_emp_tab t_emp_tab)
Where t_obj_emp = Oracle Object and t_emp_tab is a PLSQL Table of binary_integer
How can we pass a parameter that is declared as an Oracle Object type to a Procedure having a parameter as PLSQL Record type?
Eg:
Procedure C(p_obj_dept t_obj_dept)
Procedure D(p_dept_rec t_dept_rec)
Where t_obj_dept = Oracle Object having 2 fields (deptid, deptname) and t_dept_rec is declared in package specification as t_dept_rec with 2 fields (deptid, deptname).
Kindly help with some example.
Thanks in advance
The following compiles for me and appears to do what you want:
CREATE OR REPLACE TYPE t_obj_emp AS OBJECT (
emp_id INTEGER,
emp_name VARCHAR2(100)
);
/
CREATE OR REPLACE TYPE t_obj_dept AS OBJECT (
dept_id INTEGER,
dept_name VARCHAR2(100)
);
/
CREATE OR REPLACE PACKAGE my_pkg AS
TYPE t_emp_tab IS TABLE OF t_obj_emp INDEX BY BINARY_INTEGER;
TYPE t_dept_rec IS RECORD (
dept_id INTEGER,
dept_name VARCHAR2(100)
);
PROCEDURE A(p_obj_emp t_obj_emp);
PROCEDURE B(p_emp_tab t_emp_tab);
PROCEDURE C(p_obj_dept t_obj_dept);
PROCEDURE D(p_dept_rec t_dept_rec);
END;
/
CREATE OR REPLACE PACKAGE BODY my_pkg AS
PROCEDURE A(p_obj_emp t_obj_emp)
IS
v_emp_tab t_emp_tab;
BEGIN
v_emp_tab(1) := p_obj_emp;
B(v_emp_tab);
END;
PROCEDURE B(p_emp_tab t_emp_tab) IS BEGIN NULL; END;
PROCEDURE C(p_obj_dept t_obj_dept)
IS
v_dept_rec t_dept_rec;
BEGIN
v_dept_rec.dept_id := p_obj_dept.dept_id;
v_dept_rec.dept_name := p_obj_dept.dept_name;
D(v_dept_rec);
END;
PROCEDURE D(p_dept_rec t_dept_rec) IS BEGIN NULL; END;
END;
/
Note that there isn't any 'convenient' way to create one object/record from another, even if they have identical members (such a t_obj_dept and t_dept_rec in the example above). You must copy across the values of all the fields individually.
You say that t_emp_tab is a "PLSQL Table of binary_integer". I'm guessing you mean that it's a PL/SQL Table of some type indexed by binary_integer, as it's far more common to index these tables by binary_integer than it is to store binary_integers in them. For the example above I've assumed that it's a table of t_obj_emps. If it's not, you'll have to map the t_obj_emp object to whatever type of object or record t_emp_tab is a table of.

Using an Oracle Table Type in IN-clause - compile fails

Simply trying to get a cursor back for the ids that I specify.
CREATE OR REPLACE PACKAGE some_package AS
TYPE t_cursor IS REF CURSOR;
TYPE t_id_table IS TABLE OF NVARCHAR(38) INDEX BY PLS_INTEGER;
PROCEDURE someentity_select(
p_ids IN t_id_table,
p_results OUT t_cursor);
END;
CREATE OR REPLACE PACKAGE BODY some_package AS
PROCEDURE someentity_select(
p_ids IN t_guid_table,
p_results OUT t_cursor)
IS
BEGIN
OPEN p_results FOR
SELECT *
FROM someschema.someentity
WHERE id IN (SELECT column_value FROM TABLE(p_ids)); - fails here
END;
END;
Note: someschema.someentity.id is a NVARCHAR2(38)
PL/SQL: ORA-00382: expression is of wrong type
PL/SQL: ORA-22905: cannot access rows from a non-nested table item
Where am I going wrong?
In Oracle versions prior to 12.2 you can only SELECT from a collection type that is defined in the database via a CREATE TYPE statement, not an associative array:
CREATE TYPE t_id_table IS TABLE OF NVARCHAR(38);
CREATE OR REPLACE PACKAGE some_package AS
PROCEDURE someentity_select(
p_ids IN t_guid_table,
p_results OUT SYS_REFCURSOR);
END;
CREATE OR REPLACE PACKAGE BODY some_package AS
PROCEDURE someentity_select(
p_ids IN t_guid_table,
p_results OUT SYS_REFCURSOR)
IS
BEGIN
OPEN p_results FOR
SELECT *
FROM someschema.someentity
WHERE id IN (SELECT column_value FROM TABLE(p_ids));
END;
END;
This is an index-by table, which is a PL/SQL type.
You can only use SQL types in the SQL engine of Oracle. Or PL/SQL types, that Oracle can hack around to look like SQL types.
You can have a simple array-like collection and use it as a result. (no index by)
type TGuidList is table of NVarchar(38);
But the best compatibility and stability, you get by declaring it as a global SQL type and use that inside your package:
create type TGuidList is table of NVarchar(38);
Edit: You will not need an NVarChar for a GUID, will you? A good ol' VarChar should do the trick just fine.

Resources