Return 2 values from a PL-SQL function - oracle

How can i return 2 values from a PL-SQL function?

I would not advocate creating a function with an OUT parameter for the second value, because I like to think of functions as a pure concept: a function performs an operation on one or more inputs to produce one output. It shouldn't change any of its arguments or have any other "side effects".
So if you need two outputs, write a procedure instead:
procedure get_sqrt_and_half
( p_input number
, p_sqrt OUT number
, p_half OUT number
)
is
begin
p_sqrt := sqrt(p_input);
p_half := p_input/2;
end;

A function can only return a single SQL type, but that can be a user-defined type with multiple values. I'd need to know more about the actual end requirements before I'd recommend this as a solution, but it is a possibility.
create or replace type a_b is object (a number, b number);
/
create or replace function ret_a_b return a_b is
begin
return a_b(1,2);
end;
/
select ret_a_b from dual;
select d.rab.a, d.rab.b from (select ret_a_b rab from dual) d;

You can return one value directly and another one as an OUT parameter. Or you return a record that contains both values. The first option is, in most cases, simpler to do.

**If you are wanting to use it in SQL, then you would need a pipelined function e.g.**
CREATE OR REPLACE TYPE myemp AS OBJECT
( empno number,
ename varchar2(10),
job varchar2(10),
mgr number,
hiredate date,
sal number,
comm number,
deptno number
);
CREATE OR REPLACE TYPE myrectable AS TABLE OF myemp ;
enter code here
CREATE OR REPLACE FUNCTION pipedata(p_min_row number, p_max_row number) RETURN myrectable PIPELINED IS
v_obj myemp := myemp(NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
BEGIN
FOR e IN (select *
from (
select e.*
,rownum rn
from (select * from emp order by empno) e
)
where rn between p_min_row and p_max_row)
LOOP
v_obj.empno := e.empno;
v_obj.ename := e.ename;
v_obj.job := e.job;
v_obj.mgr := e.mgr;
v_obj.hiredate := e.hiredate;
v_obj.sal := e.sal;
v_obj.comm := e.comm;
v_obj.deptno := e.deptno;
PIPE ROW (v_obj);
END LOOP;
RETURN;
END;
SQL> select * from table(pipedata(1,5));

Try using OUT parameters:
create or replace function f(a IN NUMBER, b OUT NUMBER) RETURN NUMBER IS
BEGIN
b := a;
RETURN a;
END f;

Related

How to give the result of a pipelined function as a default parameter?

I have created these 4 types :
record type myrecord1 is (...)
record type myrecord2 is (...)
table tableofmyrecord1 is table of myrecord1;
table tableofmyrecord2 is table of myrecord2;
and 2 functions :
function a(k in tableofmyrecord2) return packageName.tableofmyrecord1 PIPELINED;
function b return packageName.tableofmyrecord2 PIPELINED;
I can effectively give a default parameter ; null, the table empty, a table that I have create, but I can give directly the result of a of pipelined function.
function a return packageName.tOfmyrecord(k in tableofmyrecord2 :=package.b) PIPELINED
This does'nt work.
This solution does'nt work too.
declare
defaultArgOFA :=package.b;
function a return packageName.tOfmyrecord(k in tableofmyrecord2 :=defaultArgOFA) PIPELINED
same error
PLS-00653: aggregate/table functions are not allowed
This error says that you cannot call pipelined functions using PLSQL, so you can't directly write v:=a(). But you can use SQL. Below is an example where pipelined b gets input from pipelined a and multiplies salaries.
Package:
create or replace package pkg is
type tr is record (id int, name varchar2(10), sal int);
type tt is table of tr;
function a return tt pipelined;
function b(par in tt) return tt pipelined;
end pkg;
Body:
create or replace package body pkg is
function a return tt pipelined is
v_tr tr;
begin
v_tr.id := 1; v_tr.name := 'Mark'; v_tr.sal := 100;
pipe row (v_tr);
v_tr.id := 2; v_tr.name := 'Pete'; v_tr.sal := 120;
pipe row (v_tr);
return;
end;
function b(par in tt) return tt pipelined is
v_tr tr;
begin
for i in 1..par.last loop
v_tr := par(i);
v_tr.sal := v_tr.sal * 10;
pipe row (v_tr);
end loop;
return;
end;
end pkg;
And this test worked for me:
select * from table(pkg.b(pkg.a));
Result:
ID NAME SAL
------ ---------- ----------
1 Mark 1000
2 Pete 1200

how to look up another field in Oracle Function

Table 1
ID
----------
1
2
3
4
5
Table 2
ID Desc
------------------------------
A1 Apple
A2 Pear
A3 Orange
I am trying to create a Function in Oracle, so that it add the prefix 'A' in Table 1, and after that I want to look up in Table 2 to get the DESC returned. It has to be a function.
Thank you!!!
You may use the following for creation of such a function :
Create or Replace Function Get_Fruit( i_id table2.description%type )
Return table2.description%type Is
o_desc table2.description%type;
Begin
for c in ( select description from table2 where id = 'A'||to_char(i_id) )
loop
o_desc := c.description;
end loop;
return o_desc;
End;
where
no need to include exception handling, because of using cursor
instead of select into clause.
using table_name.col_name%type for declaration of data types for
arguments or variables makes the related data type of the columns
dynamic. i.e. those would be able to depend on the data type of the
related columns.
the reserved keywords such as desc can not be used as column names
of tables, unless they're expressed in double quotes ("desc")
To call that function, the following might be preferred :
SQL> set serveroutput on
SQL> declare
2 i_id pls_integer := 1;
3 o_fruit varchar2(55);
4 begin
5 o_fruit := get_fruit( i_id );
6 dbms_output.put_line( o_fruit );
7 end;
8 /
Apple
PL/SQL procedure successfully completed
I am not sure with your question- Are you trying to achieve something like this:-
CREATE OR REPLACE FUNCTION Replace_Value
(
input_ID IN VARCHAR2
) RETURN VARCHAR2
AS
v_ID varchar(2);
BEGIN
begin
SELECT distinct a.ID into v_id from Table 2 a where a.ID in (select 'A'||b.id from table1 b where b.id=input_ID);
exception
when others then
dbms_output.put_line(sqlcode);
end;
RETURN v_id;
END Replace_Value;
Are you trying for something like this?
CREATE OR replace FUNCTION replace_value (table_name IN VARCHAR2,
input_id IN INTEGER)
RETURN VARCHAR2
AS
v_desc VARCHAR(20);
BEGIN
SELECT descr
INTO v_desc
FROM table2
WHERE id = 'A' || input_id
AND ROWNUM = 1; -- only needed if there are multiple rows for each id.
RETURN v_desc;
END replace_value;
You may also add an exception handling for NO_DATA_FOUND or INVALID_NUMBER

how can we return records from pl/sql stored procedure without taking out parameter

My Question is "How can we return multiple records from pl/sql stored procedure without taking OUT parameter".I got this doubt because if we are using cursors or refcursor in out parameter it may degrade performance.So what is the solution??
As OldProgrammer wrote, i think the performance of a cursor wouldn't be you problem. But here a Solution anyway:
You can return custom types like Table of number. If it's only a list of numbers you could return a table of numbers. If you Want to return rows from a table you could return table of 'tablename'%ROWTYPE. But i guess you want to create some custom types.
CREATE OR REPLACE TYPE PUWB_INT.MyOrderType AS OBJECT
(
OrderId NUMBER,
OrderName VARCHAR2 (255)
)
/
CREATE OR REPLACE TYPE PUWB_INT.MyOrderListType AS TABLE OF MYORDERtype
/
Now we can use them similar to a return myNumberVariable;
Let's build a function (procedures don't have return values):
CREATE OR REPLACE FUNCTION PUWB_INT.MyFunction (SomeInput VARCHAR2)
RETURN MyOrderListType
IS
myOrderList MyOrderListType := MyOrderListType ();
BEGIN
FOR o IN (SELECT 1 AS Id, 'One' AS Name FROM DUAL
UNION ALL
SELECT 2 AS Id, 'Two' AS Name FROM DUAL)
LOOP
myOrderList.EXTEND ();
myOrderList (myOrderList.COUNT) := MyOrderType (o.Id, o.Name || '(' || SomeInput || ')');
END LOOP;
RETURN myOrderList;
END MyFunction;
/
Now we can call the function and get a table of our custom-type:
DECLARE
myOrderList MyOrderListType;
myOrder MyOrderType;
BEGIN
myOrderList := MyFunction ('test');
FOR o IN myOrderList.FIRST .. myOrderList.LAST
LOOP
myOrder := myOrderList (o);
DBMS_OUTPUT.put_line ('Id: ' || myOrder.OrderId || ', Name: ' || myOrder.OrderName);
END LOOP;
END;
Be aware, that the calling schema, has to know the type.

How to pass multiple values for a parameter in a PIPELINED table function in oracle

I have created a function in Oracle with 2 parameters. So when I run the query I want to pass multiple values to each parameter.
I tried using below changes in the query:
coul_1 in ('||par1||') and colu_2 in ('||par2||')
But it is not fetching the data.
How to fetch the data when I give multiple values to different declared parameters.Eg:
select * from table(fun_name('val1','val2'))
val1 will have a1,a2,a3
val2 will have b1,b2,b3
Here is the function code:
CREATE OR REPLACE FUNCTION JOBRUN_STATUS_MONITOR_F(
own_name IN VARCHAR2,
status IN VARCHAR2)
RETURN JOBRUN_STATUS_EDW_1 PIPELINED
IS
L_TAB JOBRUN_STATUS_MONITOR_EDW_1;
JR_STATUS NUMBER (38);
CURSOR jobrun_1_cr (OW_N VARCHAR2, STS VARCHAR2)
IS
SELECT *
FROM JOBRUN A,
JOBMST B,
owner C
WHERE A.JOBMST_ID = B.JOBMST_ID
AND C.OWNER_NAME = OW_N
AND A.JOBRUN_STATUS = STS ;
BEGIN V_OWN_NAME := own_name;
V_STATUS := status;
IF jobrun_1_cr%ISOPEN THEN
CLOSE jobrun_1_cr;
END IF;
OPEN jobrun_1_cr (own_name, JR_STATUS);
CLOSE jobrun_1_cr;
END JOBRUN_STATUS_MONITOR_F;
/
It sounds like you want to call the function three times, passing (a1, b1), (a2, b2) and (a3, b3). One way would be to generate an inline view containing the values you want to pass, and query it including a call to your function.
Demo pipelined function:
create or replace function demo_pipefunc
( p_own_name in varchar2
, p_status in varchar2 )
return sys.dbms_debug_vc2coll
pipelined
as
l_result long;
begin
for i in 1..3 loop
l_result := p_own_name ||';'|| p_status ||';'|| i;
pipe row (l_result);
end loop;
return;
end demo_pipefunc;
Demo call:
with params (own_name, status) as
( select 'a1', 'b1' from dual union all
select 'a2', 'b2' from dual union all
select 'a3', 'b3' from dual
)
select t.*
from params
cross join table(demo_pipefunc(own_name, status)) t
Output:
COLUMN_VALUE
a1;b1;1
a1;b1;2
a1;b1;3
a2;b2;1
a2;b2;2
a2;b2;3
a3;b3;1
a3;b3;2
a3;b3;3
try this:
CREATE OR REPLACE FUNCTION JOBRUN_STATUS_MONITOR_F (own_name IN VARCHAR2, status IN VARCHAR2) RETURN JOBRUN_STATUS_EDW_1 PIPELINED
IS L_TAB JOBRUN_STATUS_MONITOR_EDW_1;
JR_STATUS NUMBER (38);
CURSOR jobrun_1_cr (OW_N VARCHAR2, STS VARCHAR2)
IS SELECT *
FROM JOBRUN A, JOBMST B, owner C
WHERE A.JOBMST_ID = B.JOBMST_ID
AND C.OWNER_NAME = OW_N
AND A.JOBRUN_STATUS = STS;
BEGIN
V_OWN_NAME := REPLACE(own_name,'"','''');
V_STATUS := REPLACE(status,'"','''');
IF jobrun_1_cr%ISOPEN THEN
CLOSE jobrun_1_cr;
END IF;
OPEN jobrun_1_cr (own_name, JR_STATUS);
CLOSE jobrun_1_cr;
END JOBRUN_STATUS_MONITOR_F;
then call the procedure like this:
select * from table(fun_name('"a1","b1","c1"','"a1","b1","c1"'));
Your procedure may have other issues with the use of undeclared variables.

How can I return multiple values in record type

I'd like to know how can I return multiple values with my PL/SQL in record type.
Below is my example code :-
CREATE OR REPLACE FUNCTION "FN_TESTING"
(
TESTING1 IN VARCHAR2
) RETURN TEST4_TEST as
TEST2 TEST4_TEST%ROWTYPE;
CURSOR TEST1 IS
SELECT '1','2' FROM DUAL;
BEGIN
OPEN TEST1;
FETCH TEST1
INTO TEST2;
CLOSE TEST1;
RETURN TEST2;
END FN_TESTING;
I do check my function, it shows me warning message that my TEST4_TEST must be declared.
Can I know what is the problem of this function? and how I do the declaration for this TEST4_TEST?
Yes we can return the record variable from PLSQL Function/Procedure. But first it must be declare.
create or replace function get_employee
(p_empl_no in employee.empl_no%type)
return employee%rowtype
as
l_cust_record employee%rowtype;
begin
select * into l_cust_record from employee
where empl_no = p_empl_no;
return(l_cust_record);
end;
/
Think TEST4_TEST as a variable which is of TYPE Record. This variable is just like NUMBER, VARCHAR, DATE. Only difference being that these are already defined by Oracle but in case of Collections and Records we have to define our own.
As per your example it seems that you want to return a record with 2 numbers values then you should define as follow
CREATE OR REPLACE PACKAGE TEST4_TEST1
AS
TYPE TEST4_TEST Is record
(
COL1 INTEGER,
COL2 INTEGER
);
END;
CREATE OR REPLACE FUNCTION FN_TESTING (testing1 IN VARCHAR2)
RETURN TEST4_TEST1.test4_test
AS
test3 TEST4_TEST1.test4_test;
CURSOR test2
IS
SELECT '1', '2' FROM DUAL;
A
BEGIN
OPEN test2;
FETCH test2 INTO test3;
CLOSE test2;
RETURN test3;
END fn_testing;
Try this also:
declare TYPE t_deptrec IS RECORD
(
name dept.dname%type,
location dept.loc%type
);
CURSOR c_emp is
select ename,deptno from emp;
r_dept t_deptrec;
function getDept(p_deptno dept.deptno%type) return t_deptrec is
r_dept t_deptrec;
begin
select dname,loc into r_dept
from dept where deptno = p_deptno;
return r_dept;
end;
BEGIN
for r_emp in c_emp
loop
r_dept := getDept(r_emp.deptno);
dbms_output.put_line(r_emp.ename || ',' || r_dept.name || ',' || r_dept.location);
end loop;
END;

Resources