how to call an oracle procedure and a function inside another procedure - oracle

I have a situation, where I need to do the following.
Step1: Call a procedure with the given input values and get the 2 output values.
step2: Call the function with input parameters along with one of the output value from Step1(procedure call)
Step3: Extract the output value from the return value of Step2.
Please help, how to handle this situation.
Thanks

A very basic example, with made up variable names, data types (all numbers for simplicity) and procedure/function names and signatures:
create or replace procedure wrapper_proc as
-- define local variables; use appropriate data types!
l_input_1 number;
l_input_2 number;
l_output_1 number;
l_output_2 number;
l_result number;
begin
-- Step1: Call a procedure with the given input values and get the 2 output values.
l_input_1 := 42;
l_input_2 := 128;
your_proc (l_input_1, l_input_2, l_output_1, l_output_2);
-- l_output_1 and l_output_2 are not set by that first procedire
-- step2: Call the function with input parameters along with one of the output value from Step1(procedure call)
-- assuming same two original inuts, and one of the procedure outputs...
l_result := your_func (l_input_1, l_input_2, l_output_2);
--Step3: Extract the output value from the return value of Step2.
-- do something unspecified with l_result
dbms_output.put_line('Final result was: ' || l_result);
end;
/
Or if you want to pass the input values into that wrapper procedure:
create or replace procedure wrapper_proc (
-- arguments; use appropriate data types!
p_input_1 number,
p_input_2 number
) as
-- define local variables; use appropriate data types!
l_output_1 number;
l_output_2 number;
l_result number;
begin
-- Step1: Call a procedure with the given input values and get the 2 output values.
your_proc (p_input_1, p_input_2, l_output_1, l_output_2);
-- l_output_1 and l_output_2 are not set by that first procedire
-- step2: Call the function with input parameters along with one of the output value from Step1(procedure call)
-- assuming same two original inuts, and one of the procedure outputs...
l_result := your_func (p_input_1, p_input_2, l_output_2);
--Step3: Extract the output value from the return value of Step2.
-- do something unspecified with l_result
dbms_output.put_line('Final result was: ' || l_result);
end;
/

Related

The compiler ignores NOCOPY in these cases

I read about IN OUT and NOCOPY. Then I encountered NOCOPY use cases but I was not able to get it. Can anybody explain these with examples? Thanks in advance.
The actual parameter must be implicitly converted to the data type of the formal parameter.
The actual parameter is the element of a collection.
The actual parameter is a scalar variable with the NOT NULL constraint.
The actual parameter is a scalar numeric variable with a range, size, scale, or precision constraint.
The actual and formal parameters are records, one or both was declared with %ROWTYPE or %TYPE, and constraints on corresponding fields differ.
The actual and formal parameters are records, the actual parameter was declared (implicitly) as the index of a cursor FOR LOOP statement, and constraints on corresponding fields differ.
The subprogram is invoked through a database link or as an external subprogram.
The basic principle is that PL/SQL will honour the NOCOPY directive as long as the value we pass can be used as provided, without transformation, and is addressable by the called program. The scenarios you list are circumstances where this is not the cases. I must admit a couple of these examples made me think, so this is a worthwhile exercise.
The first four examples call this toy procedure.
create or replace procedure tst2 (p1 in out nocopy t34%rowtype) is
begin
p1.id := 42;
end;
/
Case 1: The actual parameter must be implicitly converted to the data type of the formal parameter.
declare
n varchar2(3) := '23';
begin
tst(n);
dbms_output.put_line(n);
end;
/
Case 2: The actual parameter is the element of a collection.
declare
nt sys.odcinumberlist := sys.odcinumberlist(17,23,69);
begin
tst(nt(2));
dbms_output.put_line(to_char(nt(2)));
end;
/
Case 3: The actual parameter is a scalar variable with the NOT NULL constraint.
declare
n number not null := 23;
begin
tst(n);
dbms_output.put_line(to_char(n));
end;
/
Case 4: The actual parameter is a scalar numeric variable with a range, size, scale, or precision constraint.
declare
n number(5,2) := 23;
begin
tst(n);
dbms_output.put_line(to_char(n));
end;
/
The next example uses this table ...
create table t34 (id number not null, col1 date not null)
/
...and toy procedure:
create or replace procedure tst2 (p1 in out nocopy t34%rowtype) is
begin
p1.id := 42;
end;
/
Case 5 : The actual and formal parameters are records, one or both was declared with %ROWTYPE or %TYPE, and constraints on corresponding fields differ.
declare
type r34 is record (id number, dt date);
r r34;
begin
r.id := 23;
r.dt := to_date(null); --trunc(sysdate);
tst2(r);
dbms_output.put_line(to_char(r.id));
end;
/
The next example uses this package spec...
create or replace package pkg is
type r34 is record (id number, dt date);
end;
/
...and toy procedure:
create or replace procedure tst3 (p1 in out nocopy pkg.r34) is
begin
p1.id := p1.id + 10;
end;
/
Case 6: The actual and formal parameters are records, the actual parameter was declared (implicitly) as the index of a cursor FOR LOOP statement, and constraints on corresponding fields differ.
begin
for j in ( select * from t34) loop
tst3(j);
dbms_output.put_line(to_char(j.id));
end loop;
end;
/
The last example uses a remote version of the first procedure.
Case 7: The subprogram is invoked through a database link or as an external subprogram.
declare
n number := 23;
begin
tst#remote_db(n);
dbms_output.put_line(to_char(n));
end;
/
There are working demos of the first six cases on db<>fiddle here.

How to pass an array type in plsql package specification?

I am new to plsql. I am trying to put two scripts under the same package. These scripts deal with arrays. How do I pass an array into the procedure? If I am to declare the array, do I do it in the specification or the body? I am trying this right now but it doesn't work.
CREATE PACKAGE cop_cow_script AS
PROCEDURE COP_COW_DATALOAD_V2(arr_claims VARRAY(15000) OF VARCHAR2(10), arr_sql VARRAY(500) OF VARCHAR2(1000));
END cop_cow_script;
As you see I want to pass in those two arrays.
You need to declare types in the package specification and use them as parameter types in the procedure declaration:
CREATE OR REPLACE PACKAGE cop_cow_script AS
TYPE arr_claims_t IS VARRAY(15000) OF VARCHAR2(10);
TYPE arr_sql_t IS VARRAY(500) OF VARCHAR2(1000);
PROCEDURE COP_COW_DATALOAD_V2(arr_claims arr_claims_t, arr_sql arr_sql_t);
END cop_cow_script;
/
EDIT - below is an example of the package body - the procedure loops through elements of it's first parameter and prints them using DBMS_OUTPUT.PUT_LINE
PROCEDURE COP_COW_DATALOAD_V2(arr_claims arr_claims_t, arr_sql arr_sql_t)
IS
BEGIN
FOR i IN arr_claims.FIRST .. arr_claims.LAST
LOOP
DBMS_OUTPUT.PUT_LINE( arr_claims( i ) );
END LOOP;
END;
END cop_cow_script;
/
An then you can initialize them and pass to the procedure invocation for example in this way (this is an anonymous block that declares two variables, initializes them and invokes the procedure passing both parameters to it:
DECLARE
my_array1 cop_cow_script.arr_claims_t := cop_cow_script.arr_claims_t();
my_array2 cop_cow_script.arr_sql_t := cop_cow_script.arr_sql_t();
BEGIN
my_array1.extend;
my_array1( 1 ) := 'string 1';
my_array2.extend;
my_array2( 1 ) := 'string 2';
cop_cow_script.COP_COW_DATALOAD_V2( my_array1, my_array2 );
END;
/
First off, I'm hard-pressed to imagine a situation where you'd actually want to use a PL/SQL varray rather than a nested table or an associative array. There really isn't a benefit to declaring a fixed size collection.
Whatever sort of collection type you want, you'd need to declare the collection type either in the package specification or at the SQL level (depending on the type of collection) separate from the procedure specification
CREATE PACKAGE cop_cow_script AS
TYPE arr_claims IS varray(15000) of varchar(10);
TYPE arr_sql IS varray(500) of varchar(1000);
PROCEDURE COP_COW_DATALOAD_V2(p_claims arr_claims,
p_sql arr_sql);
END cop_cow_script;

Array not working correctly

I have a function that takes in 1 parameter, abc(parameter1 IN varchar2)
In the parameter I will be taking in a string that is comma delimited:
E.g Abc('1,2,a')
Type vartype is varray(10) of varchar2(50);
X1 vartype:= vartype (parameter1);
For X in X1.count loop
Dbms_output.put_line(x1(X));
End loop;
The DBMS Output gives me
1,2,a
Instead of
1
2
A
Is there anyway I can solve this?
For my understanding your function parameter will be single value.
If you are mentioned varray, you should give format like ('1','2','a','b')
For example :-
declare
Type vartype is varray(10) of varchar2(50);
X1 vartype:=vartype ('1','2','a','b');
begin
For X in 1..X1.count loop
Dbms_output.put_line(x1(x));
End loop;
end;
/
Above query will help you to understand concepts of Varray
You are passing a varchar2 variable to the varray and it's considered the first paramter; so your array contains only one element (the content of parameter1). You must split the string into substring before passing to the varray.
Here is an extract from Oracle documentation
DECLARE TYPE ProjectList IS VARRAY(50) OF VARCHAR2(16);
accounting_projects ProjectList;
BEGIN
accounting_projects := ProjectList('Expense Report', 'Outsourcing', 'Auditing');
END;
For splitting a string into substring you can check some solutions here

Handle the result of procedure A in procedure B

I have procedure A, which has some in and out parameters. I can't change parameters or code of first procedure, but I need to get different result. I decided to create procedure B, which will pass additional parameter. I want to call procedure A from this procedure and handle result of procedure A in it. I know type of parameters, which procedure A returns.
I need somethink like this:
procedure A(id in number, names out names_array) is
begin
// procedure A body
// filling names var
end;
procedure B (id in number, myLimit in number, names out names_array) is
temp_names names_array;
i integer;
begin
A(id => id, names => temp_names);
while i < myLimit loop
names(i) := temp_names(i);
end loop;
end;
So the problem is in handling names (the result of procedure A). How I can do this correctly? Maybe with some select ... limit myLimit query?
Definition of names_array:
TYPE names_array IS TABLE OF VARCHAR2 (40)
INDEX BY BINARY_INTEGER;

oracle stored procedure OUT parameter vs. postgresql pl/pgsql function OUT parameter

I'm trying to port oracle stored procedures (plsql) to a postgresql function (pl/pgsql).
In oracle I can define a stored procedure with IN and OUT parameters.
CREATE OR REPLACE PROCEDURE MY_TEST(foo IN NUMBER,
bar OUT NUMBER)
IS
BEGIN
bar := 1
END
This will store a value of 1 in the variable that is passed to the stored procedure.
I can call it as follows:
DECLARE
outValue NUMBER ;
BEGIN
Exec MY_TEST(10, outValue);
DBMS_OUTPUT.PUT_LINE('Value Returned Is : '||outValue) ;
END ;
In Postgresql (pl/pgsql) I can define a function like this one:
CREATE OR REPLACE FUNCTION MY_TEST(foo IN NUMBER,
bar OUT NUMBER)
BEGIN
bar := 1
END;
$body$
LANGUAGE PLPGSQL;
However I can not use the out parameter the same way as I could in oracle.
In postgresql the OUT parameter defines the return value.
In oracle stored procedures don't have return values, but instead write the output into the variable that is passed in the call
Is there something I overlooked, that would permit me to use pl/pgsql functions in a similar way as the stored procedure is used in the example above?
Any hints are greatly appreciated.
In PostgreSQL, PL/pgSQL or SQL functions take parameter values and return values.
They do not take pointers or references - so that the value of the referenced address could be manipulated.
You could do something like that in theory with a C-language function, where you can pass values by reference. However, de facto, you cannot. The manual warns:
Warning
Never modify the contents of a pass-by-reference input value. If you
do so you are likely to corrupt on-disk data, since the pointer you
are given might point directly into a disk buffer. The sole exception to this rule is explained in Section 35.10.
In short: what you are trying to do is impossible in PostgreSQL.
You don't need an OUT parameter for Postgres:
CREATE OR REPLACE FUNCTION MY_TEST(foo IN integer)
returns integer
as
$body$
BEGIN
return 1;
END;
$body$
LANGUAGE PLPGSQL;
Then use it like this:
DO $body$
DECLARE
result integer;
begin
result := my_test(42);
raise notice 'Return value is: %', result;
end;
$body$
Take a read of this.
http://www.postgresonline.com/journal/archives/129-Use-of-OUT-and-INOUT-Parameters.html
In summary given the following stored function and the anonymous block to test this is one approach that the article will show.
CREATE OR REPLACE FUNCTION fn_plpgsqltestout(param_subject text,
OUT subject_scramble text, OUT subject_char text)
AS
$$
BEGIN
subject_scramble := substring($1, 1,CAST(random()*length($1) As integer));
subject_char := substring($1, 1,1);
END;
$$
LANGUAGE 'plpgsql' VOLATILE;
DO $body$
DECLARE
o_subject_scramble TEXT;
o_subject_char TEXT;
begin
--Option1
SELECT (fn_plpgsqltestout('This is a test subject')).* INTO o_subject_scramble,o_subject_char;
--Option2
SELECT (fn_plpgsqltestout('This is a test subject')).subject_scramble INTO o_subject_scramble;
SELECT (fn_plpgsqltestout('This is a test subject')).subject_char INTO o_subject_char;
--Option3
o_subject_scramble := (fn_plpgsqltestout('This is a test subject')).subject_scramble;
o_subject_char := (fn_plpgsqltestout('This is a test subject')).subject_char;
raise notice 'Return value is: %', o_subject_scramble;
raise notice 'Return value is: %', o_subject_char;
end;
$body$

Resources