error on calling function by procedure - oracle

I coded for the function, and calling procedure below. Both of coding is possible to execute, but when I exec ProdDetails(10010), it show error. Can anybody know what are problems?
create or replace function ProdCheck(
f_product_id in number)
return varchar IS
f_product_name varchar(30);
f_product_price number;
begin
select product_name, price into f_product_name, f_product_price
from product
where product_id = f_product_id;
return f_product_name;
end;
create or replace procedure ProdDetails(
sp_product_id in number
)IS
f_product_id number;
sp_name varchar(30);
sp_price number;
begin
f_product_id := ProdCheck(sp_product_id);
if f_product_id > 0 then
dbms_output.put_line('Product Name : '||sp_name);
dbms_output.put_line('Product Price : '||sp_price);
else
dbms_output.put_line('Product not in the database!');
end if;
end;

Your function prodcheck takes a product_id and returns the product_name. Then in the procedure you call the function, you feed the function a product_id (everything fine so far), but then assign the return value from the function, which is the product name, to the variable f_product_id, which you declared as number. Obviously that won't work. And, indeed, the function and the procedure are both syntactically correct; only when you put them together will this fail, and only at runtime since Oracle doesn't strictly enforce data types (if the product name was '1000' instead, perhaps the function would execute OK - and produce garbage results since it would interpret this product name as the product id instead).
You query your table in the function to check if the product id is valid, and you return the name. In the procedure, you could assign the return value from the function to sp_name. The function does not return the price though (it can't - a function cannot return more than one value), so you can't display the price in the procedure. You could query the table again in the procedure, but that seems pretty senseless; it would be better to combine everything into a single procedure. (You probably don't need the check at all - if the product id doesn't exist in the table, you will get a "no rows" exception and you can use that instead of prodcheck.)

Related

Returning the collection Oracle SQL

So I have a function which I am trying to return a bunch of mobile numbers for each team. The problem is, the numbers being returned are all the same rather than being different for each team (see output)
screen print of output
Could you give any pointers to where I may be going wrong?
Thanks.
CREATE OR REPLACE TYPE BODY TeamType AS
MEMBER FUNCTION get_tel_num (numberType IN VARCHAR2)RETURN VARCHAR2 IS
prefix VARCHAR2(5);
d_code VARCHAR2(4);
main_number VARCHAR2(7);
CURSOR team_cursor IS
SELECT ta.intl_pref, ta.dial_code, ta.p_number
FROM Team t, Table(t.tel_nos) ta
WHERE ta.number_type = numberType;
BEGIN
IF NOT team_cursor%ISOPEN THEN
OPEN team_cursor;
END IF;
LOOP
FETCH team_cursor INTO prefix, d_code, main_number;
EXIT WHEN team_cursor%NOTFOUND;
RETURN prefix ||'-'|| d_code ||'-'|| main_number;
END LOOP;
CLOSE team_cursor;
END;
END;
Looking at the design on your function's signature:
Input: numberType (e.g. mobile)
Output: VARCHAR2 (i.e. the number)
Given that, and the definition of your WHERE statement at the team_cursor, there is no way for your function to know whom this number is for. Therefore, based on your LOOP body, you just return the first result of your cursor, which happens to be the same number with every call you make to the function.
One solution to that is to add one more parameter to your function that takes an identifier of the team member to which the inquired phone number belongs. Use this parameter in your WHERE statement of the team_cursor to get the number of that particular team member.
You might want to return a cursor so that the calling program can fetch all of the rows.
http://docs.oracle.com/database/121/LNPLS/static.htm#LNPLS494

Function returns null instead of expected value

When I call my function for a value that does not exist in the table:
SELECT pacjent_usun_PK_ERROR(5) from dual
(PESEL='5' does not exist in my table)
The function returns NULL.
But when I pass a valid value:
SELECT pacjent_usun_PK_ERROR(1) from dual
(PESEL='1' exists in my table)
The function returns 1.
Here is my function:
CREATE OR REPLACE FUNCTION pacjent_usun_PK_ERROR
( PES IN NUMBER )
RETURN NUMBER
IS
ILE NUMBER;
ZMIENNA NUMBER;
BEGIN
SELECT PACJENTID INTO ZMIENNA FROM PACJENT WHERE PESEL=PES;
SELECT COUNT(PRZYJECIEID) INTO ILE FROM PRZYJECIE_NA_ODDZIAL
WHERE PACJENTID=ZMIENNA and rownum=1;
RETURN ILE;
END;
When I test my last SELECT from my function, with manual inserted PRZYJECIEID
(PACJENTID='1111' does not exist in my table)
SELECT COUNT(PRZYJECIEID) FROM PRZYJECIE_NA_ODDZIAL WHERE PACJENTID='1111' and rownum=1;
Result is: 0
and testing with PACJENTID='1' (exist in my table)
Result is: 1
I am trying to understand why the function returns NULL instead of 0 when there are no rows.
I am using Oracle SQL Developer
I don't know what causes that behavior; if I can figure it out I will post again.
In any case, the following version should work. I created a similar function using the tables EMP and DEPT in the standard (Oracle) SCOTT schema, and the change I show below worked in that setting. Essentially I eliminate the intermediate variable; I do everything in a single query and assignment.
create or replace FUNCTION pacjent_usun_PK_ERROR
(PES IN NUMBER)
RETURN NUMBER
IS
ILE NUMBER;
BEGIN
SELECT COUNT(PRZYJECIEID) INTO ILE
FROM PRZYJECIE_NA_ODDZIAL
WHERE PACJENTID=(SELECT PACJENTID FROM PACJENT WHERE PESEL=PES)
and rownum=1;
RETURN ILE;
end;
Added: To clarify... if select into... returns no rows, PL/SQL should raise an exception, specifically the NO_DATA_FOUND exception. The mystery that is worth investigating is why PL/SQL does not raise this exception in this case.
Final edit - it appears this has always been the behavior in PL/SQL functions, as opposed to PL/SQL procedures. NO_DATA_FOUND is an exception (it can be handled in the usual manner) but it is not raised. See this old discussion on AskTOM: https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::p11_question_id:10321465390114
This means that when the first select into returns no data, the function is in an unhandled exception state. This is why the second select into doesn't result in a count of 0 - execution doesn't even get that far.
This also means that you may keep your function code exactly as it is, but you need to add an exception handling block:
exception
when no_data_found then return 0;
right before end;
I tested this on my "mock-up" of your function (in the SCOTT schema) and it works.

oracle how to transfer SQL into member function

one of my assignment question is:
The method AvgReviewScore() returns the average review score for an album excluding scores from anonymous reviewers, i.e. reviews with null REVIEWER_NAME.
So firstly i wrote SQL:
(This is oriented database assignment)
select deref(b.album).title
,round(avg(b2.reviewscore),2)
from album_artist_table b,table(deref(b.album).review) b2
where deref(b.artist).aname like '%Joe%'
and b2.reviewername is not null
group by deref(b.album).title;
Can i ask how to translate this sql to member function?
I try to create a type called:AvgReviewScore_type
and then i create type body member function as following:
create or replace TYPE BODY ALBUM_TYPE AS
member function AvgReviewScore return AvgReviewScore_type AS
AVGtable AvgReviewScore_type := AvgReviewScore_type(null,null);
BEGIN
select deref(b.album).title,round(avg(b2.reviewscore),2)
into AVGtable
from album_artist_table b,table(deref(b.album).review) b2
where b2.reviewername is not null
group by deref(b.album).title;
return AVGtable;
END AvgReviewScore;
END;
But it doesn't work, is there anybody know about the reason?
I guess your question has incomplete information and depending on whatever given see the below example. I hope it would help you.
Create type exmpl_type as object (
num number,
member function func(p in number) return number
);
/
create type body exmpl_type as
member function func(p in number)
return number is
begin
return num/p;
end func;
end;
/

Trigger with a function Oracle

I need to make a trigger to insert 'A' in case of a approved student and 'R' for a not approved student. I need to use a function that returns 'A' or 'R'. Here is my function:
create or replace function check_grade
(grade in number, frequency in number) return varchar2
as
result varchar2(1) default '';
begin
if(grade>=6) and (frequency>=0.75)then
result := 'A';
else
result := 'R';
end if;
return result;
end;
And here is my trigger:
create or replace trigger situation
before
insert on student
for each row
begin
select check_grade(grade, frequency)
into : new.situation
from dual;
end;
When I try to execute I always get an error. I don't have idea in what i can do!
User-defined functions are allowed in triggers, and your function looks OK.
One definite problem with your trigger is that it mentions grade and frequency, which are variable names, and they haven't been declared. I'll assume you probably meant to use the inserted column values instead: :new.grade and :new.frequency.
Also, I've always set "new" values using simple variable assignment (:new.value := blah instead of select blah into :new.value from dual). Your way may work (or may not - I don't know), but the simple variable assignment syntax is a lot shorter and it's easy enough to read.
So try replacing these three lines:
select check_grade(grade, frequency)
into : new.situation
from dual;
... with this one line:
:new.situation := check_grade(:new.grade, :new.frequency);
If this doesn't work then please update the question and replace "I always get an error" with I get error ORA-<actual-error-number>. Include the stack trace, which will show if the function or the trigger is throwing.
The function isn't needed here; it can be replaced by a CASE expression:
create or replace trigger situation
before insert on student
for each row
begin
:new.situation := CASE
WHEN grade >= 6 and frequency >= 0.75 THEN 'A'
ELSE 'R';
END;
end situation;

sql command not ended properly after executing the function

for example I have created a table with table name 'aaa' with four columns act_num, clear_balance, available_balance, total_balance and I have inserted some values.
The function
deb_amount withdraws money from a bank account. It accepts an account
number and an amount of money as parameters. It uses the account number to
retrieve the account balance from the database, then computes the new balance. If this
new balance is less than zero then the function jumps to an error routine; otherwise,
it updates the bank account.
create or replace function deb_amount(p_act_num VARCHAR2, p_amount number )
return number as
declare
v_old_amount number;
v_new_amount number;
e_over_drawn exception;
begin
select clear_balance into v_old_amount from aaa where act_num=p_act_num;
v_new_amount:=v_old_amount-p_amount;
if v_old_amount<p_amount then
raise e_over_drawn;
else
update aaa set clear_balance=v_new_amount,available_balance=v_new_amount,total_balance=v_new_amount where act_num=p_act_num;
end if;
commit;
return clear_balance;
exception
when e_over_drawn then
rollback;
end;
it will compile, but with warnings.
If I want to execute the 'select * from deb_amount(1,100)' it show error.
sql command not ended properly.
Thank you.
You need to call function using dual. Ex:
select deb_amount(1,100) from dual;
or using a variable in plsql block
declare
l_return number;
begin
l_return:=deb_amount(1,100);
end;
It looks like you might be running several commands as a scipt, but haven't ended the function properly. The / after the function creation has to be on a line on its own, and at the start of the line:
create or replace function deb_amount(p_act_num VARCHAR2,
p_amount number)
return number as
declare
v_old_amount number;
v_new_amount number;
e_over_drawn exception;
begin
select clear_balance into v_old_amount
from aaa where act_num=p_act_num;
v_new_amount:=v_old_amount-p_amount;
if v_old_amount<p_amount then
raise e_over_drawn;
else
update aaa set clear_balance=v_new_amount,
available_balance=v_new_amount,
total_balance=v_new_amount
where act_num=p_act_num;
end if;
commit;
return clear_balance;
exception
when e_over_drawn then
rollback;
end;
/
show errors
select deb_account('1', 1) from dual;
The show errors will tell what actual compilation errors you got. It looks like it will complain about the return as you don't have a local clear_balance variable, but you can use v_new_amount instead here. You need to return something after the rollback too, or raise an exception which might be more useful.
As Manjunatha said, your query then needs to call the function properly, with the from clause referencing a table, rather than the function itself.
You have a bigger problem with the concept though; you can't call a function that does DML (insert, update, delete) from SQL, only from a PL/SQL block. Generally DML should be done from a procedure rather than a function, if it has to be done in PL/SQL at all.

Resources