Returning multiple values from a function in oracle - oracle

I have the following table:
T_TYPE_ID T_TYPE T_TYPE_PRICE T_TYPE_START_DATE T_TYPE_END_DATE
1 student 10.95 01.04.2015 00:00:00 30.06.2015 00:00:00
2 Concession 5.5 01.04.2015 00:00:00 30.06.2015 00:00:00
I need to:
Create FUNC_get_ t_type_end_date function
This function should contain the following input parameter: t_type_p and the following output parameters: t_type_price_p and t_type_end_date_p. It should return 1 if having a record with t_type as t_type_p otherwise return 0. In addition, in the case of having a record, it should assign the latest t_type_end_date to t_type_end_date_p and t_type_price to t_type_price_p. Please note that t_type_end_date_p can be null; it means that the associated price is currently valid.
I have written the following code:
CREATE OR REPLACE FUNCTION FUNC_get_t_type_end_date
( t_type_p IN VARCHAR2)
RETURN NUMBER
AS
cnum NUMBER;
CURSOR cr1 IS
SELECT t_type
FROM ticket_type
WHERE t_type = t_type_p;
BEGIN
OPEN cr1;
FETCH cr1 INTO cnum;
IF cr1%NOTFOUND THEN
cnum := 0;
END IF;
CLOSE cr1;
RETURN cnum;
END;
I did not get any clue on how to return multiple values from a function. I am using oracle.
Hint:
create or replace FUNCTION FUNC_get_ t_type_end_date (…)
return number
as
-- define a variable to return a number and assign 0 to it
-- define a cursor to obtain t_type_price, t_type_end_date of the given t_type_p. The t_type_end_date values should be sorted in descending order – to do so the first record will contain either null or the latest of t_type_end_date
BEGIN
-- open cursor
-- fetch the first record from the cursor to t_type_price_p and t_type_end_date_p
-- if (having a record) then …
-- close cursor
RETURN …
END;

It is not possible to return more than one variable from function. However, it is possible to return a customized variable (i.e. record) type that contains multiple values. To do this, you need first to define type contains the three variables you want to return as follows:
TYPE new_type is record(cnum number, t_type_end_date_p timestamp, t_type_price_p timestamp);
Then you can use it in your function as follows:
CREATE OR REPLACE FUNCTION FUNC_get_t_type_end_date ( t_type_p IN VARCHAR2)
RETURN new_type AS
new_type_variable newtype;
CURSOR cr1 IS
SELECT t_type
FROM ticket_type
WHERE t_type = t_type_p;
BEGIN
OPEN cr1;
FETCH cr1 INTO cnum;
IF cr1%NOTFOUND THEN
SELECT 0, null, null into new_type_variable from dual;
ELSE
SELECT 1, cr1.t_type_end_date, cr1.t_type_price into new_type_variable from dual;
END IF;
CLOSE cr1;
RETURN new_type_variable ;
END;

Try this:
create or replace function func_get_t_type_end_date(t_type_p in varchar2, t_type_price_p out number, t_type_end_date_p out date) return number
as
cnum NUMBER;
CURSOR cr1 IS
SELECT t_type_price,t_type_end_date
FROM ticket_type t
WHERE t_type = t_type_p
and not exists (select 1
from ticket_type t2
where t2.t_type = t.t_type
and t2.t_type_end_date>t.t_type_end_date);
BEGIN
OPEN cr1;
FETCH cr1 INTO t_type_price_p,t_type_end_date_p;
IF cr1%NOTFOUND THEN
cnum := 0;
else
cnum := 1;
END IF;
CLOSE cr1;
RETURN cnum; end;
Example of using:
set serveroutput on
declare
v_result number;
v_type_price_p number;
v_type_end_date_p date;
begin
v_result:=func_get_t_type_end_date(t_type_p=>'student',t_type_price_p=>v_type_price_p,t_type_end_date_p=>v_type_end_date_p);
dbms_output.put_line('Result: '||v_result);
dbms_output.put_line('Price: '||v_type_price_p);
dbms_output.put_line('End date: '||v_type_end_date_p);
end;
/

Related

Oracle read and return data from cursor

I'm trying to create a stored procedure in Oracle that has one input parameter and one output variable and, in case of results, return a dataset to my .Net Application. The main issue is that I can't change the signature of the procedure and need to do if condition to validate if exist records or not.
The main issue that i've being struggled is with cursors (to execute and return the information), and to count the results of the select.
Here is an example of what i'm doing to try to retrieve the data.
CREATE PROCEDURE SP_Testing (v_input IN VARCHAR2(50), v_OutID NUMBER(1))
AS
TYPE v_record_botoes IS RECORD (
v_dummy_col1 VARCHAR2(50),
v_dummy_col2 VARCHAR2(250)
);
TYPE table_botoes IS TABLE OF v_record_botoes;
tt_botoes table_botoes;
v_ref_cursor SYS_REFCURSOR;
CURSOR v_cursor IS
(SELECT dt.v_dummy_col1,
dt.v_dummy_col2
FROM dummy_table dt
WHERE v_dummy_col3 = v_input);
v_check NUMBER;
BEGIN
tt_botoes := table_botoes();
v_check := 0;
FOR v_row IN v_cursor
LOOP
tt_botoes.extend;
tt_botoes(tt_botoes.COUNT) := v_row;
END LOOP;
v_check := tt_botoes.COUNT;
-- condition that need to know the nr of records of the select
IF v_check = 0 THEN
v_OutID := 0;
ELSE
v_OutID := 1;
OPEN v_ref_cursor FOR
SELECT *
FROM tt_botoes; -- also tryed "FROM TABLE (tt_botoes)" and "FROM TABLE (cast(tt_botoes AS table_botoes))"
-- return dataset to .net application
DBMS_SQL.RETURN_RESULT(v_ref_cursor)
END IF;
END;
Already tryed to convert the v_cursor into a sys_refcursor to be outputed by the DBMS_SQL package but didn't get anywhere.
Also i've tried to create a temporary table to hold the information, but then have a concurrency issue.
Any idea what i'm doing wrong, or any other possible solution to solve this issue?
Thanks in advance
Completely untested because I do not have the environment to test it but I would structure this quite differently, see below (I am assuming that the missing out in the specification is just a typo)
CREATE PROCEDURE SP_Testing (v_input IN VARCHAR2(50), v_OutID **out** NUMBER(1))
AS
v_ref_cursor SYS_REFCURSOR;
v_check NUMBER;
BEGIN
select count(1)
into v_check
from dummy_table dt
WHERE v_dummy_col3 = v_input
-- condition that need to know the nr of records of the select
IF v_check = 0 THEN
v_OutID := 0;
ELSE
v_OutID := 1;
OPEN v_ref_cursor FOR
SELECT dt.v_dummy_col1,
dt.v_dummy_col2
FROM dummy_table dt
WHERE v_dummy_col3 = v_input
-- return dataset to .net application
DBMS_SQL.RETURN_RESULT(v_ref_cursor)
END IF;
END;

How to count rows from two columns?

I try to create the function login that takes customer number(pnr) and password from same table. Its fine to create function but test crashes with following eror:
ORA-00904: "P_PASSWD": invalid identifier
create or replace function logga_in(
p_pnr bankkund.pnr%type,
p_passwd bankkund.passwd%type
)
return number
as
v_resultat number(1);
begin
select count(pnr) into v_resultat
from bankkund
where p_pnr = pnr
and p_passwd = passwd;
return 1;
exception
when no_data_found then
return 0;
end;
There is one other problem with your code not suggested in the comments, A count function from a select into will not raise a NO_DATA_FOUND exception. You may use an IF condition on count or do something like this, which is preferable
CREATE OR REPLACE FUNCTION logga_in (
p_pnr bankkund.pnr%TYPE,
p_passwd bankkund.passwd%TYPE
) RETURN NUMBER AS
v_resultat NUMBER(1);
BEGIN
SELECT 1 --do not use count if you wish to handle no_data_found
INTO v_resultat FROM
bankkund WHERE pnr = p_pnr AND
passwd = p_passwd
AND ROWNUM = 1; --Add this
RETURN 1;
EXCEPTION
WHEN no_data_found THEN
RETURN 0;
END;
Now, as far as calling the procedure is concerned, there are various options available including using bind variable
VARIABLE p_pnr number --use the datatype of bankkund.pnr%TYPE
VARIABLE p_passwd VARCHAR2(10) --use the datatype of bankkund.passwd
SELECT logga_in(:p_pnr,:p_passwd) FROM dual;
Or substitution variable
SELECT logga_in('&p_pnr','&p_passwd') FROM dual;
Give inputs when prompted.
Or use PL/SQL block
DECLARE
v_res INT;
v_pnr bankkund.pnr%type := 12892; --or appropriate value
p_passwd bankkund.passwd%type := some_passwd';
BEGIN
v_res := logga_in();
If v_res = 1 THEN
do_something_u_want; --call or execute appropriate action.
END IF;
END;
/

Oracle function, how to pass parameters to cursor and use it

Here is one of oracle functions. There is a cursor called c_adv_course_credit which receives 2 parameters. These 2 parameters are using the where statement:
WHERE
-- year
cc.year = p_year AND
-- rela_pk
cc.sequence_number = p_sequence_number AND
cc.closed_ind = 'N';
When I run it in oracle sql developer:
SET SERVEROUTPUT ON
variable res varchar2(200);
EXECUTE :res := advp_test_cursor(2018, 92919);
select :res from dual;
The result text is always "not working"
Here is the full function (not working):
CREATE OR REPLACE Function SISD_OWNER.advp_test_cursor (
p_sequence_number IN NUMBER, -- rela_pk
p_year IN NUMBER -- year
)
RETURN VARCHAR2
IS
v_return_var VARCHAR2(300) := 'not working';
CURSOR c_adv_course_credit (
p_sequence_number IN NUMBER,
p_year IN NUMBER
)
IS
SELECT
cc.EXTERNAL_COURSE_CD
FROM
adv_course_credit cc
WHERE
cc.year = p_year AND
-- rela_pk
cc.sequence_number = p_sequence_number AND
cc.closed_ind = 'N';
BEGIN
FOR v_at_rec IN c_adv_course_credit(p_sequence_number, p_year) LOOP
v_return_var := v_at_rec.EXTERNAL_COURSE_CD;
DBMS_OUTPUT.PUT_LINE('?output = ' || v_return_var);
EXIT;
END LOOP;
RETURN v_return_var;
END;
If I change the cursor to use hard-coded numbers the function works and returns actual result.
WHERE
-- year
cc.year = 2018 AND
-- rela_pk
cc.sequence_number = 92919 AND
cc.closed_ind = 'N';
Your function is defined as (ignoring the data types):
advp_test_cursor(p_sequence_number, p_year)
but you're calling it as
advp_test_cursor(2018, 92919);
which has the arguments the wrong way round. You either need to flip them:
advp_test_cursor(92919, 2018);
or use named parameter notation:
advp_test_cursor(p_year=>2018, p_sequence_number=>92919)
or indeed combine both:
advp_test_cursor(p_sequence_number=>92919, p_year=>2018)
You do not need to use cursors:
CREATE OR REPLACE Function SISD_OWNER.advp_test_cursor (
p_sequence_number IN adv_course_credit.sequence_number%TYPE,
p_year IN adv_course_credit.year%TYPE
) RETURN adv_course_credit.EXTERNAL_COURSE_CD%TYPE
IS
v_return_var adv_course_credit.EXTERNAL_COURSE_CD%TYPE;
BEGIN
SELECT EXTERNAL_COURSE_CD
INTO v_return_var
FROM adv_course_credit
WHERE year = p_year
AND sequence_number = p_sequence_number
AND closed_ind = 'N';
DBMS_OUTPUT.PUT_LINE('?output = ' || v_return_var);
RETURN v_return_var;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RETURN 'Not working';
END;

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