How to execute procedure with input parametr (Varray OF INT)? - oracle

CREATE OR REPLACE TYPE list_of_int IS
VARRAY(10) OF INT;
CREATE OR REPLACE PROCEDURE my_procedure(
in_lista in list_of_int
)
AS
...
exec my_procedure( [1,2,3] );
How to execute procedure with input parametr VARRAY?

You can do something like this
CREATE OR REPLACE TYPE list_of_int IS
VARRAY(10) OF INT;
/
CREATE OR REPLACE PROCEDURE my_procedure(
in_lista in list_of_int
)
AS
begin
for i in 1..in_lista.count
loop
dbms_output.put_line( in_lista(i) );
end loop;
end;
/
exec my_procedure( list_of_int(1, 2, 3) );
/
Practically, though, I have yet to encounter an instance where it made sense to declare a varray type. It would almost certainly make more sense to declare a nested table type which doesn't limit the number of elements you can have in your collection (well, I think you're limited to 2^32 or whatever you can fit in PGA but if you're doing something seriously wrong if you get close to that). I can't think of a situation where I'd want to have code that intentionally dies if someone wants to pass an 11 element list.
CREATE OR REPLACE TYPE int_t IS
table OF INT;
/
CREATE OR REPLACE PROCEDURE my_procedure(
in_lista in int_t
)
AS
begin
for i in 1..in_lista.count
loop
dbms_output.put_line( in_lista(i) );
end loop;
end;
/
exec my_procedure( int_t(1, 2, 3) );
/

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;
/

call a procedure with table as IN parameters

I have the following code which works very nice:
declare
v_order_id oe.orders.order_id%type := 1;
v_order_item pkg_order_management.to_order_list := pkg_order_management.to_order_list();
begin
v_order_item.extend(2);
v_order_item(1).product_id := 2289;
v_order_item(1).quantity := 2;
v_order_item(2).product_id := 2058;
v_order_item(2).quantity := 5;
pkg_order_management.prc_create_order(240, v_order_item, v_order_id);
dbms_output.put_line('it was created the order: ' || v_order_id);
end;
but I want to call the pkg_order_management.prc_create_order procedure like
declare
v_order_id oe.orders.order_id%type := 1;
begin
pkg_order_management.prc_create_order(240, ((2289, 2),(2058, 5)), v_order_id);
dbms_output.put_line('it was created the order: ' || v_order_id);
end;
Here is the types definition from the package:
type t_order_item is record
( product_id oe.order_items.product_id%type
, quantity oe.order_items.quantity%type);
type to_order_list is table of t_order_item;
When I call the procedure as in 2nd case, I receive the following error:
PLS-00306: wrong number or types of arguments in call to
'PRC_CREATE_ORDER'
Surely, my call type is wrong but I have no idea how to solve this.
Can you give me a hint, please?
PL/SQL record types are not object-oriented constructs. So we can't use them as flexibly as we can actually Objects.
If you want to pass an inline array you need to define your types using SQL:
create or replace type t_order_item is object
( product_id number
, quantity number);
/
create or replace type to_order_list is table of t_order_item;
/
Note that this means you can no longer use %TYPE referencing to define the attributes of t_order_item.
Now your call to the procedure will look like this:
begin
pkg_order_management.prc_create_order(240
, to_order_list(t_order_item(2289, 2)
, t_order_item(2058, 5)
), v_order_id);
end;
/
The proper call would be this:
pkg_order_management.prc_create_order(
240,
pkg_order_management.to_order_list(
t_order_item(2289, 2),
t_order_item(2058, 5)
),
v_order_id
);
... but do you really want such an ugly code?
pkg_order_management.prc_create_order(240, v_order_item, v_order_id); looks more beautiful to me.
You need to use OBJECT not RECORD.
Please red this:
https://docs.oracle.com/cd/B19306_01/appdev.102/b14261/objects.htm
Then you can use this nice notation:
CREATE TYPE t_order_item AS OBJECT (
product_id NUMBER,
quantity NUMBER
);
/
CREATE TYPE to_order_list IS TABLE OF t_order_item;
/
DECLARE
v_order_id NUMBER;
PROCEDURE prc_create_order (a NUMBER, order_list to_order_list, o_order_id OUT NUMBER)
IS
BEGIN
NULL;
END;
BEGIN
prc_create_order(240, to_order_list(
t_order_item(2289, 2),
t_order_item(2058, 5)
)
, v_order_id);
END;
/

ORACLE stored procedure - Store query result

I have the following stored procedure:
CREATE OR REPLACE PROCEDURE SP
(
query IN VARCHAR2(200),
CURSOR_ OUT SYS_REFCURSOR
)
AS
row_ PROCESSED_DATA_OBJECT;
processed PROCESSED_DATA_TABLE;
BEGIN
.....
END;
with
CREATE TYPE processed_data_obj AS OBJECT(
id INTEGER,
value FLOAT
);
/
CREATE OR REPLACE TYPE processed_data_table AS TABLE OF processed_data_obj;
/
I call the stored procedure passing the query to be executed as input parameter.
The query is something like that:
SELECT A,B FROM TABLE WHERE
where A,B and TABLE are not fixed (defined at runtime during java program execution), so I don't know their values in advance.
How could I fetch/store each returned row in my structure?
processed PROCESSED_DATA_TABLE;
Thanks
This is one way you can process a dynamically generated query into a user defined type. Note that, in order for this to work, the structure of your query (columns) must match the data type structure of your type (attributes) otherwise you're in for trouble.
CREATE TYPE processed_data_obj AS OBJECT(
ID INTEGER,
VALUE FLOAT,
constructor FUNCTION processed_data_obj RETURN self AS result
);
/
CREATE OR REPLACE TYPE BODY processed_data_obj IS
constructor FUNCTION processed_data_obj RETURN self AS result IS
BEGIN
RETURN;
END;
END;
/
CREATE OR REPLACE TYPE processed_data_table AS TABLE OF processed_data_obj;
/
CREATE OR REPLACE PROCEDURE sp (
p_query IN VARCHAR2
) AS
cursor_ sys_refcursor;
processed processed_data_table := processed_data_table();
BEGIN
OPEN cursor_ FOR p_query;
loop
processed.EXTEND;
processed(processed.count) := processed_data_obj();
fetch cursor_ INTO processed(processed.count).ID, processed(processed.count).VALUE;
exit WHEN cursor_%notfound;
dbms_output.put_line(processed(processed.count).ID||' '||processed(processed.count).VALUE);-- at this point do as you please with your data.
END loop;
CLOSE cursor_; -- always close cursor ;)
processed.TRIM; -- or processed.DELETE(processed.count);
END sp;
I noticed that, originally, you did put CURSOR_ as an output parameter in your stored procedure, if that is still your goal, you can create your procedure as:
CREATE OR REPLACE PROCEDURE sp (
p_query IN VARCHAR2,
cursor_ out sys_refcursor
) AS
processed processed_data_table := processed_data_table();
BEGIN
OPEN cursor_ FOR p_query;
loop
processed.EXTEND;
processed(processed.count) := processed_data_obj();
fetch cursor_ INTO processed(processed.count).ID, processed(processed.count).VALUE;
exit WHEN cursor_%notfound;
dbms_output.put_line(processed(processed.count).ID||' '||processed(processed.count).VALUE);-- at this point do as you please with your data.
END loop;
-- cursor remains open
processed.TRIM; -- or processed.DELETE(processed.count);
END sp;
In this case just be conscious about handling your cursor properly and always close it when you're done with it.

Can we use a table type parameter as a default null parameter in PLSQL?

I have a record type as follows,
TYPE x_Rec IS RECORD(
master_company x_tab.master_company%TYPE,
report_trans_type x_tab.report_trans_type%TYPE,
balance_version_id x_tab.balance_version_id%TYPE,
reporting_entity x_tab.reporting_entity%TYPE,
year_period_from x_tab.year_period%TYPE,
year_period_to x_tab.year_period%TYPE,
journal_id x_tab.journal_id%TYPE,
row_id x_tab.row_id%TYPE);
and I have created a table type using this record:
TYPE x_rec_tab IS TABLE OF x_Rec INDEX BY PLS_INTEGER;
I want to use this table type in a procedure as a default null parameter.
PROCEDURE x_Balance___(x_param IN NUMBER,
x_rec_ IN x_rec_tab default null)
IS
BEGIN
...My code
END;
It gives the following error message
PLS-00382: expression is of the wrong type
I resolved this by using CAST(null as /*your_type*/) in the Procedure's signature.
For instance, in your case, it will be something like this:
PROCEDURE x_Balance (x_param IN NUMBER,
x_rec_ IN x_rec_tab default cast(null as x_rec_tab))
Then, within the procedure, you just need to check if x_rec_ has elements by using the count method.
This way works for me.
You can't do that with an associative array, as that can never be null. You would get the same error if you tried to assign null to a variable of type x_rec_tab. They also don't have constructors, so you can't use an empty collection instead.
You can do this will a varray or more usefully for your situation a nested table:
create or replace package p42 as
TYPE x_Rec IS RECORD(
master_company x_tab.master_company%TYPE,
report_trans_type x_tab.report_trans_type%TYPE,
balance_version_id x_tab.balance_version_id%TYPE,
reporting_entity x_tab.reporting_entity%TYPE,
year_period_from x_tab.year_period%TYPE,
year_period_to x_tab.year_period%TYPE,
journal_id x_tab.journal_id%TYPE,
row_id x_tab.row_id%TYPE);
-- no index-by clause, so nested table not associative array
TYPE x_rec_tab IS TABLE OF x_Rec;
end p42;
/
Package P42 compiled
show errors
No errors.
create or replace package body p42 as
PROCEDURE x_Balance___(x_param IN NUMBER,
x_rec_ IN x_rec_tab default null)
IS
BEGIN
--...My code
null;
END;
PROCEDURE dummy IS
l_rec_tab x_rec_tab;
BEGIN
l_rec_tab := null;
END;
end p42;
/
Package Body P42 compiled
show errors;
No errors.
You could also default to an empty collection instead:
PROCEDURE x_Balance___(x_param IN NUMBER,
x_rec_ IN x_rec_tab default x_rec_tab())
IS
...
That doesn't really help you much if you have other code that relies on the type being an associative array of course.
Old question but still might be helpful.
You can create a function:
function empty_tab
return x_rec_tab
as
l_tab x_rec_tab;
begin
return l_tab;
end empty_tab;
This way you can (notice that empty_tab is used as default parameter):
PROCEDURE x_Balance___(x_param IN NUMBER,
x_rec_ IN x_rec_tab default empty_tab)
IS
BEGIN
...My code
END;
This is a repeat of #ManuelPerez answer, but I just feel that it could have been explained better.
Create this procedure, casting your optional variable to your datatype like this:
CREATE OR REPLACE PROCEDURE Test_Procedure (
txt_ IN VARCHAR2,
col_formats_ IN dbms_sql.varchar2a DEFAULT cast(null as dbms_sql.varchar2a) )
IS BEGIN
Dbms_Output.Put_Line (txt_);
FOR i_ IN 1 .. 10 LOOP
IF col_formats_.EXISTS(i_) THEN
Dbms_Output.Put_Line (i_ || ' Exists');
ELSE
Dbms_Output.Put_Line (i_ || ' DOES NOT Exist');
END IF;
END LOOP;
END Test_Procedure;
The reason this beats the accepted answer is that it doesn't require you to change the datatype of the incoming variable. Depending on your circumstance, you may not have the flexibility to do that.
Now call your procedure like this if you have a variable to feed the procedure:
DECLARE
txt_ VARCHAR2(100) := 'dummy';
arr_ dbms_sql.varchar2a;
BEGIN
arr_(4) := 'another dummy';
Test_Procedure (txt_, arr_);
END;
Or like this if you don't:
DECLARE
txt_ VARCHAR2(100) := 'dummy';
BEGIN
Test_Procedure (txt_);
END;
Your output will look something like this:
dummy
1 DOES NOT Exist
2 DOES NOT Exist
3 DOES NOT Exist
4 Exists
5 DOES NOT Exist
6 DOES NOT Exist
7 DOES NOT Exist
8 DOES NOT Exist
9 DOES NOT Exist
10 DOES NOT Exist

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

Resources