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

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;

Related

Can i use REGEXP_LIKE as a condition with IF in a PL/SQL block and my string i will pass as parameter

if (NOT REGEXP_LIKE(l_hdr_high_amt_fare_tab(j).fare_basis,
L_HIGH_FARE_FLAG)) then
if l_total_fare > 13000 then
Begin
update tsapp_dwh_staging_tbl
set error_code = 'LA_QC_S10'
where sys_seq_no = p_sys_seq_no;
commit;
and my L_HIGH_FARE_FLAG is parameterized.
which is
+CHARTER(*)|+GRUPO(*)|+GROUP(*)
this is working if i hard-code this string in my code but when i pass string as parameter it wont work.
It worked with
SELECT '' || replace(a.value_text, ',', '|') || ''
FROM tmaac_app_parameters a
WHERE a.key_name = 'EMD_FLAG'
and a.module = 'IO'
and a.sub_module = 'EXTRACT'
and a.function = 'PARAMETER_PRCS';
If you just want to know whetherregexp_like will work in an if condition, then yes it will:
declare
function testit
( stringval varchar2 )
return varchar2
is
begin
if regexp_like(stringval,'^A') then
return 'Y';
else
return 'N';
end if;
end testit;
begin
dbms_output.put_line(testit('Abracadabra!'));
end;
/
Y
PL/SQL procedure successfully completed.
I'm not sure how l_high_fare_flag and tsapp_dwh_staging_tbl etc are related to the question.

Stored Procedure to get all records

I want to loop thru all the records and concatenate them into one string.
Here is the code:
create or replace PROCEDURE P_GET_TRACKING_NOS
(
P_ORDERID NUMBER,
TRACKINGNOS OUT VARCHAR2
)
IS
CURSOR C1 IS
SELECT TRACKID
FROM MULTISHIPDTL
WHERE ORDERID = P_ORDERID;
BEGIN
TRACKINGNOS := '';
FOR TRACKID_REC IN C1
LOOP
TRACKINGNOS := TRACKINGNOS + ', ' + TRACKID_REC.TRACKID;
END LOOP;
END;
Depending on how long the result is, and if it is shorter than 4000 characters, a simpler option would be to use LISTAGG, e.g.
select listagg(m.trackid, ', ') within group (order by null) result
from multishipdtl m
where m.orderid = p_orderid;
Besides, why is it a procedure? A function seems to be a better option (you can use it in SQL; a procedure with an OUT parameter requires a(n anonymous) PL/SQL block, declaring a variable which accepts the result). For example:
create or replace function f_get_tracking_nos (p_orderid in number)
return varchar2
is
retval varchar2(4000);
begin
select listagg(m.trackid, ', ') within group (order by null)
into retval
from multishipdtl m
where m.orderid = p_orderid;
return retval;
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;
/

Comparing number with varchar2

I have this function and I need to compare number with varchar.
CREATE OR REPLACE FUNCTION getOdds(i_odd in varchar2, i_id in number) return number as
begin
declare odd integer;
declare i_perecentage=0;
begin
if i_odd ='SP'
then
return (0);
end if;
odd:=round(to_number((1-i_perecentage/100)*i_odd),2);
if odd<1
then
return(i_odd);
else
return(round(odd,2));
end if;
end;
end;
/
PS: I edited function and i resolve problem with comparing , now i have another situation that i dont like..
This function returns calculated percentage of i_odd. The problem is that if i pass 0 in i_percentage in results i get result with no decimal places(for example: i_odd = 3.10 and i_percentage = 0 i get odd = 3 but if I pass i_odd = 3.10 and i_percentage = 1 i get odd = 3.10 ).
Why is on i_percentage = 0 i dont get decimal places ??
If you want to validate a varchar2 field as a number in PL/SQL, typically you'd just try converting it to a number and catch the exception.
CREATE OR REPLACE FUNCTION getOdds(i_odd in varchar2, i_id in number) return number as
odd number;
BEGIN
-- if i_odd = 'SP' (or another non-number), this will throw an ORA-01722
-- exception which will be caught in the exception block, below
odd := to_number(i_odd); -- you might want a format mask here
--... now you can use "odd" as a number
EXCEPTION WHEN INVALID_NUMBER THEN
return 0;
END;
/
You can also nest a begin..end block in the middle of your code just to catch exceptions, if that works better for you:
CREATE OR REPLACE FUNCTION getOdds(i_odd in varchar2, i_id in number) return number as
odd number;
begin
begin
odd := to_number(i_odd); -- you might want a format mask here
exception when INVALID_NUMBER then
odd := 0;
end;
--... now you can use "odd" as a number
end;
/
The reason why you can't catch the invalid_number exception is because you are declaring the input parameter as a number. When you call your function, Oracle tries to convert the string to a number first (and it fails of course, before entering your code at all).
If you change the input parameter to varchar2, then the conversions to number (implicit in this case) is done inside the function, and invalid numbers can be caught and handled as you want (here I'm just returning a different string to denote the issue):
create or replace function is_odd_even(i_num in varchar2)
return varchar2
is
begin
-- conversion to number is done here
if (mod(i_num, 2) = 0) then
return 'EVEN';
else
return 'ODD';
end if;
exception
when INVALID_NUMBER or VALUE_ERROR then
-- do something meaningful
return 'INV';
end;
Usage example:
with x as (
select '1' as val from dual
union all
select 'SP' as val from dual
union all
select '2' as val from dual
)
select x.val, is_odd_even(x.val)
from x;
Output:
1 ODD
SP INV
2 EVEN
SOLUTION:
CREATE OR REPLACE FUNCTION getOdds(i_odd in varchar2, i_id in number) return varchar2 as
odd varchar2(10);
ret_value number(4);
begin
if (i_odd ='SP') or i_odd is null then
return 'SP';
else
odd :=ROUND( TO_NUMBER( ( 1 - (play_beting.get_odds_percentage(i_id) / 100 ) ) * TO_NUMBER(i_odd) ), 2);
IF(odd < 1) THEN
ret_value := TO_NUMBER(i_odd);
ELSE
ret_value := to_char(odd,'9999.00');
END IF;
END IF;
RETURN to_char(ret_value,'9999.00');
END getOdds;

Returning multiple values from a function in 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;
/

Resources