PL/SQL function - oracle

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;

Related

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

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;

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 using the like comparison taken from function in value

I am trying to create a simple function that takes in 3 parameters, 2 numbers and a string. I have written the function but am not getting the expected results from a simple select statement when using the LIKE comparison for the string.
The select from the function below returns no rows when executed with the string input value set to ebts, but if I run this as a standalone select state it returns 2 rows which what I would expect. Have used dbms output to determine if whitespace were being passed but all looks OK.
CREATE OR REPLACE FUNCTION OPC_OP.sitezone_exists
(in_site_id IN NUMBER, in_zone_id IN NUMBER, in_mod VARCHAR2)
RETURN NUMBER
IS
v_count_rec NUMBER;
v_return NUMBER;
v_mod VARCHAR2(4) := in_mod;
BEGIN
SELECT COUNT(*)
INTO v_count_rec
FROM AW_ACTIVE_ALARMS
WHERE AW_ACTIVE_ALARMS.site_id = in_site_id
AND AW_ACTIVE_ALARMS.zone_id = in_zone_id
AND AW_ACTIVE_ALARMS.module LIKE 'v_mod%';
IF v_count_rec > 0
THEN
DBMS_OUTPUT.PUT_LINE('count'||v_count_rec||'=========='||v_mod||'=============');
v_return:= 1;
RETURN (v_return);
ELSE
DBMS_OUTPUT.PUT_LINE('count'||v_count_rec||'=========='||v_mod||'=============');
v_return:= 0;
RETURN (v_return);
END IF;
END sitezone_exists;
When passing in values 12, 12, ebts the output displayed is:
count 0 ==========ebts=============
RetVal = 0
If I run the same select subtituting only passing in the above values the query returns 2 rows - I have removed the like clause of the function and it then returns 2 rows, any idea why the like part of clause is failing to match with rows.
You are trying to match a string literal with this:
AND AW_ACTIVE_ALARMS.module LIKE 'v_mod%';
Change it to:
AND AW_ACTIVE_ALARMS.module LIKE v_mod||'%';
MarioAna has the right answer, IMO, but as an aside (which is too long for a comment), your function can be better written.
You're duplicating the dbms_output code, plus it's considered best practice to have a single RETURN in a function (although I would argue that more might be ok - one in the body and one per exception in the exception block...), so you could rewrite it as:
CREATE OR REPLACE FUNCTION OPC_OP.sitezone_exists
(in_site_id IN NUMBER, in_zone_id IN NUMBER, in_mod VARCHAR2)
RETURN NUMBER
IS
v_count_rec NUMBER;
v_return NUMBER;
v_mod VARCHAR2(4) := in_mod;
BEGIN
SELECT COUNT(*)
INTO v_count_rec
FROM AW_ACTIVE_ALARMS aaa
WHERE aaa.site_id = in_site_id
AND aaa.zone_id = in_zone_id
AND aaa.module LIKE v_mod||'%';
DBMS_OUTPUT.PUT_LINE('count '||v_count_rec||'=========='||v_mod||'=============');
IF v_count_rec > 0 THEN
v_return := 1;
ELSE
v_return:= 0;
END IF;
RETURN (v_return);
END sitezone_exists;
/

Calling a function in a before delete trigger

id like to call this function:
CREATE OR REPLACE PACKAGE orders_salary_manage2 AS
FUNCTION total_calc(p_order in NUMBER)
RETURN NUMBER;
END;
CREATE OR REPLACE PACKAGE BODY orders_salary_manage2 AS
tot_orders NUMBER;
FUNCTION total_calc(p_order in NUMBER)
RETURN NUMBER
IS
c_price product.unit_price%type;
c_prod_desc product.product_desc%type;
v_total_cost NUMBER := 0;
CURSOR c1 IS
SELECT product_desc, unit_price
FROM product
WHERE product_id IN (SELECT fk2_product_id
FROM order_line
WHERE fk1_order_id = p_order);
BEGIN
OPEN c1;
LOOP
FETCH c1 into c_prod_desc, c_price;
v_total_cost := v_total_cost + c_price;
EXIT WHEN c1%notfound;
END LOOP;
CLOSE c1;
return v_total_cost;
END;
from this trigger:
CREATE OR REPLACE TRIGGER trg_order_total
BEFORE DELETE ON placed_order
FOR EACH ROW
DECLARE
v_old_order NUMBER := :old.order_id;
BEGIN
total_calc(v_old_order);
END;
but i keep getting this error, note there is no error number just this:
Error at line 4: PL/SQL: Statement ignored
BEFORE DELETE ON placed_order
FOR EACH ROW
DECLARE
v_old_order NUMBER := :old.order_id;
BEGIN
im new to pl/sql and just not sure what is causing the problem. When a user deletes an order from the orders table the trigger should call the function to add up all the products on the order.
Thank you
(Considering your Package compiled with no errors) Use-
ret_val:= orders_salary_manage2.total_calc(v_old_order);
The ret_val must be a NUMBER since the package function total_calc returns a NUMBER. A function MUST always return its outcoume to a variable (like ret_val) depending on the type of the return value the data type of the variable must be declared.
The syntax to call Pacakaged Procedures and functions is -
<RETURN_VARIABLE> := PACKAGE_NAME.<FUNCTION_NAME>();
PACKAGE_NAME.<PROCEDURE_NAME>(); --Since Procedure never returns
Also note that if your package is in a different SCHEMA and has no PUBLIC SYNONYM then you will have to prefix the schema name like <SCHEMA>.PACKAGE_NAME.<FUNCTION_NAME>() (considering the calling schema has execute permissions on the package).
So,
CREATE OR REPLACE TRIGGER trg_order_total
BEFORE DELETE ON placed_order
FOR EACH ROW
DECLARE
v_old_order NUMBER := :old.order_id;
v_ret_val NUMBER := 0;
BEGIN
v_ret_val := orders_salary_manage2.total_calc(v_old_order);
--...Do stuff with v_ret_val
END;

Function does not return what I am expecting

In my function below I am trying to understand why it only returns BLAH if I pass in 01356666 and then a null value for anything else passed in. My expectation was that it would return BLAH regardless of what was passed in since I reset the str_mgrin_out after I do the SELECT INTO. I have been testing this in Oracle 10g.
CREATE OR REPLACE FUNCTION GET_MANAGERGIN2 (str_empgin_in IN varchar2)
RETURN varchar2
AS
str_mgrgin_out varchar2(10);
BEGIN
SELECT 'FOO' INTO str_mgrgin_out FROM dual WHERE str_empgin_in = '01356666';
str_mgrgin_out := 'BLAH';
RETURN str_mgrgin_out;
END GET_MANAGERGIN2;
/
-- Returns null but expecting BLAH
SELECT GET_MANAGERGIN2('00356666') FROM dual;
-- Returns BLAH
SELECT GET_MANAGERGIN2('01356666') FROM dual;
I'd assume that this is because if the SELECT INTO doesn't return exactly one value into str_mgrgin_out it'll throw an exception, so the str_mgrgin_out := 'BLAH'; line never gets executed.
I think the error would be ORA-01403.
Try adding an exception handler at the end as:
Exception
When Others Then
str_mgrgin_out := 'BLAH';
You might have to surround it with a BEGIN...END as well so it would be:
CREATE OR REPLACE FUNCTION GET_MANAGERGIN2 (str_empgin_in IN varchar2)
RETURN varchar2
AS
str_mgrgin_out varchar2(10);
BEGIN
BEGIN
SELECT 'FOO' INTO str_mgrgin_out FROM dual WHERE str_empgin_in = '01356666';
str_mgrgin_out := 'BLAH';
Exception
When Others THen
str_mgrgin_out := 'BLAH';
END;
RETURN str_mgrgin_out;
END GET_MANAGERGIN2;
The select matches zero rows, with raises a NO_DATA_FOUND PL/SQL exception.
However NO_DATA_FOUND isn't recognized as an SQL error, merely the end of the result set.
Therefore when used in a SELECT, a null value is returned.

Resources