Handling exception 1) when defining Oracle function 2) When Calling Oracle function - oracle

My Oracle function below(code1) have no exception handling.
Therefore if it is called(code2) with 0, error shows.
--Code 1
CREATE OR REPLACE FUNCTION TEST2
(P1 IN VARCHAR2)
RETURN NUMBER AS V_VALUE NUMBER;
BEGIN
SELECT(
SELECT 1/TO_NUMBER(P1)
FROM DUAL
)
INTO V_VALUE
FROM DUAL;
RETURN V_VALUE;
END;
/
--Code2
SELECT TEST2('0') FROM DUAL;
Please, help to add exception handling for each 1) 2) case as below.
case 1) when defining function, how to modify code1
for function to return -1 if a system exception,
including dividing by zero happen?
case 2) Without adding exception in my Oracle funtion,
how to modify code2 for the reslut to be -1 if a system excetion, including dividing zero happens in the function?

I think you are making this more complicated than it needs to be. I did not compile this code below, but should provide as an example. Regarding exceptions, it is OK to handle divide-by-zero, but hiding all other exception types is very, very bad design. Also, if I pass in test2(-1), then the result will be a valid value of -1. Are you assured your input parameter is always positive. Regardless, here is a solution which checks for a 0 parameter, and avoids the division problem. A better solution is to define TEST1 P1 as a NUMBER to begin with, and let the caller format it as needed. If not, I could pass in something like TEST2('fsfd') and get an exception.
CREATE OR REPLACE FUNCTION TEST2(P1
IN VARCHAR2)
RETURN NUMBER;
D_Result NUMBER : = -1;
BEGIN
IF P1 <> 0 THEN
D_result := 1/TO_NUMBER(P1);
END IF;
RETURN D_Result;
END
If you really want to throw a divide error, you can catch is like this:
DECLARE
result NUMBER;
BEGIN
result := test2(0);
EXCEPTION
WHEN OTHERS THEN
result := -1;
END;

#OldProgrammer shows how to prevent the exception from occurring, which is the best choice. However, if you want to allow the exception to occur and catch it in the function you could use:
CREATE OR REPLACE FUNCTION TEST2(P1 IN VARCHAR2)
RETURN NUMBER
AS
V_VALUE NUMBER;
BEGIN
BEGIN
V_VALUE := 1 / TO_NUMBER(P1);
EXCEPTION
WHEN OTHERS THEN
V_VALUE := -1;
END;
RETURN V_VALUE;
END TEST2;
or you could simplify this to
CREATE OR REPLACE FUNCTION TEST2(P1 IN VARCHAR2)
RETURN NUMBER
AS
BEGIN
RETURN 1 / TO_NUMBER(P1);
EXCEPTION
WHEN OTHERS THEN
RETURN -1;
END TEST2;

Related

Best way to manage exception in package

I have a package with a several procedures and functions, the procedures are called from an external program and in turn they call functions.
Where is the best way to manage a Exception?
For example, Prog1 call Proc1 and Proc1 call the Funct1, if in function I have an exception ("TOO MANY ROW" or "NO DATA FOUND"), which is the best way to raise a specific customized message and return immediatally to Prog1.
In this moment I have this
PROCEDURE_1
BEGIN
*code*
CALL FUNCTION_1
*code*
EXCEPTION
WHEN NO DATA FOUND THEN
*print customized message*
RETURN;
END;
FUNCTION_1
BEGIN
*code*
EXCEPTION
WHEN NO DATA FOUND THEN
RAISE;
END;
It is the best way?
Regards,
Marco
Ideally a procedure would be allowed to fail with an exception and error stack if something went totally wrong, but then the difference between a fatal error and an expected condition will vary depending on the business logic you are implementing, so it's hard to say what should happen in your particular case.
If the agreed interface is that the procedure should return a formatted message, and that message may include expected business conditions such as a product being out of stock, then you can handle that within the procedure using something like this (nonsense pseudocode to illustrate approach only):
create or replace procedure procedure_1
( p_result_message out varchar2 )
as
somevar number;
begin
do_stuff('fruit','cake');
begin
somevar := function_1('bananas');
exception
when no_data_found then
p_result_message := 'No kittens are available for this mission.';
return;
end;
p_result_message := 'Cake is available in aisle 3';
exception
when something_else then
p_result_message := 'Self-destruct sequence initiated.';
end;
(For a more purist approach you might prefer to rearrange the code so that it always reaches the end with a value for p_result_message, rather than quitting partway through if some condition pops up.)
Now you have a way to handle whatever exceptions might reasonably arise in function_1, without any special handling within the function itself.
You could also have the function raise an exception defined in a package, although then you lose the ability to define a diagnostic error message at the point of failure, and in my experience this just tends to complicate things. But to illustrate:
create or replace package starfleet
as
shield_failure exception;
warp_core_malfunction exception;
pragma exception_init(shield_failure, -20998);
pragma exception_init(warp_core_malfunction, -20999);
procedure check_status
( status_message out varchar2 );
function status
return number;
end starfleet;
create or replace package body starfleet as
function status
return number
is
status_ind number;
begin
select 1 into status_ind from dual where 1=2; -- fails with NDF
return status_ind;
exception
when no_data_found then raise shield_failure;
end status;
procedure check_status
( status_message out varchar2 )
is
status_ind number;
begin
status_ind := status();
status_message := 'Everything is fine';
exception
when warp_core_malfunction then status_message := 'Abandon ship';
when shield_failure then status_message := 'Increase power to shields';
end check_status;
end starfleet;
Now, the status() function can raise exceptions defined in the package (or any other package for that matter), and procedure check_status can catch it and decide how to handle it.
Example of calling it from SQL*Plus:
SQL> var status_message varchar2(100)
SQL>
SQL> begin
2 starfleet.check_status(:status_message);
3 end;
4 /
PL/SQL procedure successfully completed.
STATUS_MESSAGE
-------------------------
Increase power to shields

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;

PL/SQL exception handling in function

I am trying to generate DIVIDE_BY_ZERO exception in my oracle PL/SQL program. I using function but when I run my program I am getting error which is shown below. Can anyone tell me what is wrong in my program ?
code:
CREATE OR REPLACE
PACKAGE CALCULATOR AS
FUNCTION AddNumber(addend IN Number, Addend2 IN Number) RETURN NUMBER;
FUNCTION DivNumber(divend IN Number, divend2 IN Number) RETURN NUMBER;
END CALCULATOR;
/
CREATE OR REPLACE
PACKAGE BODY CALCULATOR AS
FUNCTION AddNumber(addend IN Number, Addend2 IN Number) RETURN NUMBER AS
BEGIN
return addend + addend2;
END AddNumber;
FUNCTION DivNumber(divend IN Number, divend2 IN Number) RETURN NUMBER AS
BEGIN
return divend / divend2;
EXCEPTION
WHEN ZERO_DIVIDE THEN
NULL;
END DivNumber;
END CALCULATOR;
/
select calculator.AddNumber(3,4) from dual;
select calculator.DivNumber(12,0) from dual;
output:
you can deal with exceptions like this
FUNCTION DivNumber(divend IN Number, divend2 IN Number) RETURN NUMBER AS
e_ZERO_DIVIDE EXCEPTION;
BEGIN
-- the condition
IF divend2 = 0 THEN
Raise e_ZERO_DIVIDE;
END IF;
return divend / divend2;
--Exception handling
EXCEPTION
WHEN e_ZERO_DIVIDE THEN
dbms_output.put_line('Division by 0 or null');
RETURN 1; -- or 0 or null
WHEN OTHERS THEN
dbms_output.put_line('ERROR: '||sqlerrm);
RETURN 1; -- or 0 or null
END DivNumber;
IMO you should never write code which throws an avoidable exception. There may be times when you can't avoid raising an exception but in this case you can. I suggest that you rewrite your function as follows:
FUNCTION DivNumber(numerator IN Number, denominator IN Number) RETURN NUMBER AS
BEGIN
IF NVL(denominator, 0) <> 0 THEN
return numerator / denominator;
ELSE
RETURN NULL;
END IF;
END DivNumber;
Best of luck.

Where in stored procedure I can make exit status?

I have oracle stored procedure where i check sender I'd,source system, and transaction number at the beginning of the procedure. Can I do it this way:
If Id != "aaa"
Exit -1;
Else if source = " ".
Exit -1;
Else if trans = " ".
Exit -1;
Else.
-- continues stored procedure
I appreciate any help
To rephrase your question more generally, you want a caller of your routine to know if something bad has happened inside it. There are (at least) three ways of doing this in PL/SQL.
Use an OUT parameter
Procedure cannot return a value, the way a function does, but it can set an output parameter:
CREATE PROCEDURE inner (p_id IN VARCHAR2(10), p_res OUT NUMBER)
IS
BEGIN
p_res := 0; -- default value
IF p_id = 'aaa' THEN
p_res := -1;
RETURN;
ELSE
-- do something
END IF;
END;
Then in the caller you would have:
DECLARE res NUMBER;
...
inner('aaa', res);
IF res = -1 THEN
-- panic!
END IF;
...
Use a function
Despite your seeming aversion to functions, this might be an option.
CREATE FUNCTION inner (p_id IN VARCHAR2(10))
RETURN NUMBER
IS
BEGIN
IF p_id = 'aaa' THEN
RETURN -1;
END IF;
-- do something
RETURN 0;
END;
Then in the caller:
...
IF inner('aaa') = -1 THEN
-- panic!
END IF;
...
Use an exception
Similar to other programming languages, PL/SQL has exceptions:
CREATE PROCEDURE inner (p_id IN VARCHAR2(10))
IS
BEGIN
IF p_id = 'aaa' THEN
RAISE_APPLICATION_ERROR(-20000, 'ID cannot be ''aaa''');
ELSE
-- do something
END IF;
END;
and in the caller:
...
DECLARE
panic EXCEPTION; -- declare exception
PRAGMA EXCEPTION_INIT (panic, -20000); -- assign error code to exception
...
BEGIN
inner ('aaa');
EXCEPTION
WHEN panic THEN
-- proceed to panic
END;
You are using a wrong syntax both for the IF...ELSE and for the exit.
Given that you are saying you need to get a return value, you probably need a function like this, using CASE:
create or replace function testFun ( pIn1 varchar2, pIn2 varchar2) return varchar2 is
begin
case
when pIn1 is null then
return -1;
when pIn2 = ' ' then
return -2;
else
return 999;
end case;
end;

PL/SQL function

I am new to PL/SQL. I have this error:
Error at line 5: PL/SQL: SQL Statement ignored
CREATE OR REPLACE FUNCTION calculeaza_total_incasari RETURN NUMBER IS
incasari NUMBER;
BEGIN
nrutilizatori:=calculeaza_total_utilizatori;
SELECT sum (p.pret) into incasari from rezervare r,planificare p , film f where f.idfilm=p.idfilm and r.idplanificare=p.idplanificare
RETURN incasari/nrutilizatori;
EXCEPTION
WHEN NO_DATA_FOUND THEN
dbms_output.put_line('Nu sunt incasari');
END;
How can I replace that line?
First you need to declare the nrutilizatori parameter, and initialize it like this: nrutilizatori := calculeaza_total_utilizatori();.
Don't forget to put all semicolon ; characters after select statement.
Try the following code:
CREATE OR REPLACE FUNCTION calculeaza_total_incasari RETURN NUMBER IS
incasari NUMBER;
nrutilizatori number;
BEGIN
nrutilizatori := calculeaza_total_utilizatori();
SELECT sum (p.pret) into incasari from rezervare r,planificare p , film f where f.idfilm=p.idfilm and r.idplanificare=p.idplanificare;
RETURN incasari/nrutilizatori;
EXCEPTION
WHEN NO_DATA_FOUND THEN
dbms_output.put_line('Nu sunt incasari');
END;
/
CREATE OR REPLACE FUNCTION calculeaza_total_incasari RETURN NUMBER IS
incasari NUMBER;
nrutilizatori NUMBER;
BEGIN
nrutilizatori:=calculeaza_total_utilizatori();
SELECT sum (p.pret) into incasari from rezervare r,planificare p , film f where f.idfilm=p.idfilm and r.idplanificare=p.idplanificare;
RETURN incasari;
EXCEPTION
WHEN NO_DATA_FOUND THEN
dbms_output.put_line('Nu sunt incasari');
END;
[Edit] I just noticed that this question was asked a few months back. I will leave my answer since it could be useful for other users.
Since you are using aggregate function SUM you do not need exception handler - no_data_found will never be raised. If you expect to have no rows returned, wrap SUM with NVL or else you will get NULL value, in which case the return result will also be NULL.
Other than that you did not declare nrutilizatori variable, have syntax error (you did not write ';' after the select statement) and a possible logical/numeric error - if it is possible for a function calculeaza_total_utilizatori to return 0 you will get division by zero error, and if it can return NULL your final result will also be NULL.
Another additional comment - functions have to return value in all cases, including exception handler blocks, else you will get ORA-06503 error 'Function returned without value'. This won't happen in your case because no_data_found will not be raised, but you have to keep this in mind for future development.
The complete function is as follows (without checking what calculeaza_total_utilizatori function returns), although I don't understand this function/query (nor calculeaza_total_utilizatori) - why no input variables? It looks as if you could just sum everything from planificare table, without joining it to other tables. Not sure if this is really the requirement...
CREATE OR REPLACE FUNCTION calculeaza_total_incasari RETURN NUMBER IS
incasari NUMBER;
nrutilizatori NUMBER;
BEGIN
nrutilizatori := calculeaza_total_utilizatori;
SELECT NVL(SUM(p.pret), 0)
INTO incasari
FROM rezervare r, planificare p, film f
WHERE f.idfilm = p.idfilm
AND r.idplanificare = p.idplanificare;
RETURN incasari/nrutilizatori;
END;
I think there's a quite better way to write the code (Check it ahead).
By the way, NO_DATA_FOUND exception is almost impossible to be raised, because a query with aggrupation function will always return a single row if the query doesn't have the GROUP BY clause.
CREATE OR REPLACE FUNCTION calculeaza_total_incasari RETURN NUMBER IS
incasari NUMBER;
nrutilizatori NUMBER;
ret_value NUMBER;
BEGIN
<<total_incasari_bk>>
BEGIN
nrutilizatori := calculeaza_total_utilizatori;
SELECT NVL(SUM(p.pret), 0)INTO incasari from rezervare r,planificare p , film f where f.idfilm=p.idfilm and r.idplanificare=p.idplanificare;
ret_value := incasari/nrutilizatori;
EXCEPTION
WHEN NO_DATA_FOUND THEN
dbms_output.put_line('Nu sunt incasari');
WHEN OTHERS THEN
dbms_output.put_line(SQLERRM);
END total_incasari_bk;
RETURN ret_value;
END;

Resources