PL/SQL Function with Exception Handling - oracle

I am a beginner in PL/SQL. I need to write a function with following details:
Create a function named 'find_transaction_type' that will accept the transaction_type_id as input. Based on this input, the function must return the transaction type name of type varchar.
Function name : find_transaction_type,
Input Parameter : transaction_type_id in int
Design rules:
1)If transaction type id(i.e, transaction_type_id) passed as input, matches with the id in the transaction table,then it returns the type of the given transaction_type_id.
2)If the transaction type id passed as input, does not match with the id in the transaction table,then it throws ' no_data_found' exception and displays it with the text as ' No such Type'
Note: Use variable to print the exceptions instead of 'dbms_output.put_line'
ie: umpire_name := 'No such umpire';
My Solution is:
create or replace function find_transaction_type(transaction_type_id in integer) return varchar is
transaction_type_name varchar(255);
type_id integer;
error_msg varchar(255);
begin
error_msg := 'No such Type';
select id
into type_id
from transaction_type;
if type_id = transaction_type_id
then
select type
into transaction_type_name
from transaction_type
where id = transaction_type_id;
return(transaction_type_name);
else
raise no_data_found;
end if;
exception
when no_data_found then
raise_application_error(-10403, error_msg);
end;
/
What's wrong with my code?

You do not need the first select nor do you need the if statement. Just let the query raise the no_data_found exception. Refer to types by their table's respective types.
create or replace function find_transaction_type (
transaction_type_id in transaction_type.transaction_type_id%type
)
return transaction_type.type%type is
transaction_type_name transaction_type.type%type;
begin
select type -- not a good column name
into transaction_type_name -- this should be the column name also
from transaction_type
where id = transaction_type_id;
return transaction_type_name;
exception
when no_data_found then
if transaction_type_id is null then
raise_application_error(-10403, "type argument is null");
else
raise_application_error(-10403, "type '" || transaction_type_id || "' not found");
end if;
end;

Use varchar2 instead of varchar.
Add a where clause to the first select statement.
If you do 2 no need for the if then else.

Related

PL-SQL I'm trying to use a variable value into an Insert Into - ERROR ORA-00984

I have a package that contains the following procedure:
`
PROCEDURE PRC_DO_ISCRIZIONE( P_ID_STUD IN NUMBER, P_ID_CORSO IN NUMBER)
IS
V_ID_CORSO NUMBER := NULL;
V_ID_STUD NUMBER := NULL;
V_NEXT_ID NUMBER := NULL;
EX_NO_STUD EXCEPTION;
EX_NO_CORSO EXCEPTION;
CURSOR C_LISTA_CORSI IS ( SELECT ID FROM CORSO);
CURSOR C_LISTA_STUD IS ( SELECT ID FROM STUDENTE);
BEGIN
SELECT MAX(ID) +1
INTO V_NEXT_ID
FROM ISCRIZIONE;
FOR S IN C_LISTA_STUD
LOOP
IF P_ID_STUD != S.ID
THEN RAISE EX_NO_STUD;
END IF;
END LOOP;
FOR C IN C_LISTA_CORSI
LOOP
IF P_ID_CORSO != C.ID
THEN RAISE EX_NO_CORSO;
END IF;
END LOOP;
SELECT ID
INTO V_ID_CORSO
FROM CORSO
WHERE ID = P_ID_CORSO;
SELECT ID
INTO V_ID_STUD
FROM STUDENTE
WHERE ID = P_ID_STUD;
INSERT INTO ISCRIZIONE(ID, ID_CORSO, ID_STUDENTE, DATA)
VALUES ( V_NEXT_ID , V_ID_CORSO, V_ID_STUDENTE, SYSDATE);
EXCEPTION
WHEN EX_NO_STUD
THEN DBMS_OUTPUT.PUT_LINE('NESSUNO STUDENTE CORRISPONDE ALL''ID INSERITO');
RETURN;
WHEN EX_NO_CORSO
THEN DBMS_OUTPUT.PUT_LINE('NESSUN CORSO CORRISPONDENTE ALL''ID INSERITO');
RETURN;
END PRC_DO_ISCRIZIONE;
`
But when I launch the create package body I get the error ora-00984: column not allowed in this case
the goal is that given a student ID and a course ID,if they exists in their relative tables, the procedure adds to the ENROLLMENT(Iscrizione) table a row containing student id, course id and date
The source of your error appears to be the identifier V_ID_STUDENTE in the VALUES clause of your INSERT statement.
You don't have a local variable named V_ID_STUDENTE, but you do have one named V_ID_STUD. Try replacing V_ID_STUDENTE with V_ID_STUD.

ora-01422 error in SELECT INTO statements

I have one row in my Company_Person_all view named by 'YENER UZUN' in EMPLOYEE_NAME column (I already want only one result). When I send parameter to this function (fname, instead of using 'YENER UZUN') I encounter
ORA-01422:exact fetch returns more than requested number of rows ...
What should I do to prevent this error? Also when I write the code below ('YENER UZUN', instead of fname) it's ok it doesn't give me an error.
FUNCTION Get_Calistigi_Santiye_By_Fname(fname IN varchar2)
RETURN varchar2
IS
temp_ varchar2(100);
BEGIN
select free_field6
into temp_
from company_person_all
where employee_name = 'YENER UZUN';
DBMS_OUTPUT.put_line(temp_);
RETURN temp_;
END;
I solved it by changing 'fname' parameter name to 'xyz'. 'fname' was being used a RECORD instance name by other procedures and functions in the package.
So that when i changed parameter name the error instantly fixed.
Mostly, Using a cursor instead of select .. into is a shortcut to avoid ORA-01422 with a proper order by(asc [default] / desc) clause due to which of the records prefer for your business logic(last or first record ) as in the following :
FUNCTION Get_Calistigi_Santiye_By_Fname( fname company_person_all.employee_name%type )
RETURN company_person_all.free_field6%type
IS
temp_ company_person_all.free_field6%type;
BEGIN
for c in
(
select free_field6 ff6
from company_person_all
where employee_name = fname --> 'YENER UZUN'
order by work_date_time
)
loop
temp_ := c.ff6;
end loop;
dbms_output.put_line(temp_);
RETURN temp_;
END;

Exception Handling SQL

I created a user defined function that calculates the quantity in stock for a product
CREATE OR REPLACE FUNCTION function_quantityInStock(
oldProductQuantity IN INTEGER,
orderedQuan IN INTEGER)
RETURN INTEGER
IS
v_newQuantity INTEGER;
v_oldQuantity INTEGER;
v_orderedQuan INTEGER;
BEGIN
v_newquantity := oldProductQuantity - orderedQuan;
RETURN v_newquantity;
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('Please check your data.');
END function_quantityInstock;
I then create a trigger that updates the table by calling the function
CREATE OR REPLACE TRIGGER TRIGGER_QUANTITY AFTER INSERT ON ordered_product
FOR EACH ROW
DECLARE
v_oldQuantity INTEGER;
BEGIN
SELECT PRODUCT_QUANTITYINSTOCK INTO v_oldQuantity
FROM product
WHERE product_id = :NEW.product_id;
UPDATE PRODUCT
SET product_quantityinstock =
function_quantityINSTOCK(v_oldQuantity, :NEW.ORDERED_PRODUCTQUANTITY)
WHERE product_id = :NEW.product_id;
END;
I want to display a message when the user enters invalid data but my exception block doesnt do that.
I use the following anonyomus block to test the function:
SET SERVEROUTPUT ON;
DECLARE
v_productID ordered_product.product_id%TYPE:= &ProductID;
v_orderID ordered_product.order_id%TYPE:=&OrderID;
v_orderedQuan ordered_product.ordered_productQuantity%TYPE := &OrderedProductQuantity;
v_totalCost ordered_product.ordered_productTotalCost%TYPE := '&TotalCost';
BEGIN
INSERT INTO ordered_product VALUES
(v_orderID, v_productID, v_orderedQuan, v_totalCost);
dbms_output.put_line('A new record has been inserted.');
EXCEPTION
WHEN NO_DATA_FOUND THEN
dbms_output.put_line('Invalid data!');
WHEN VALUE_ERROR THEN
dbms_output.put_line('Error! Please check your values.'|| SQLERRM);
END;
You might be missing exec dbms_output.enable(10000) and therefore not be seeing the output.
You should throw the exception using
raise_application_error(-20000, 'Please check your data.');
Also it's bad practice to catch all exceptions using when others and then ignore them.
If I were you, I wouldn't rely on dbms_output.put_line to pass information around. Instead, rely on the standard exception handling, e.g. RAISE or RAISE_APPLICATION_ERROR. Also, given that you're trying to find the new quantity, I'd move the select on the product table into the function, something like:
CREATE OR REPLACE FUNCTION function_quantityinstock(p_product_id IN product.product_product_id p_orderedquan IN INTEGER)
RETURN INTEGER IS
v_newquantity INTEGER;
e_not_enough_stock EXCEPTION;
e_not_enough_stock_num INTEGER := -20001;
e_no_product_exists_num INTEGER := -20002;
PRAGMA EXCEPTION_INIT(e_not_enough_stock, -20001);
BEGIN
SELECT product_quantityinstock - orderedquan
INTO v_newquantity
FROM product
WHERE product_id = :new.product_id;
IF v_newquantity < 0
THEN
RAISE e_not_enough_stock;
END IF;
RETURN v_newquantity;
EXCEPTION
WHEN no_data_found THEN
raise_application_error(-e_no_product_exists_num,
'No product exists for product_id = ' || product_id);
-- no need to check for TOO_MANY_ROWS if product_id is the primary/unique key on the product table.
WHEN e_not_enough_stock THEN
raise_application_error(e_not_enough_stock_num,
'Not enough stock present to fulfil the order for product_id = ' ||
product_id);
WHEN OTHERS THEN
raise_application_error(SQLCODE,
'Unexpected error occurred whilst finding new quantity for product_id = ' ||
product_id || ': ' || SQLERRM);
END function_quantityinstock;
/
You'd need to change the function name though, to reflect the action that it's doing, since it's returning the new quantity in stock...

How to validate record type as function input in PL/SQL

I am using Oracle 11g in Windows environment. I am not a thorough PL/SQL Developer. My situation is like this.
I am using a package, need to validate the logging in user. Not checking table column directly to do this.
create or replace package Configuration_pkg as
TYPE user_rec IS RECORD
(email VARCHAR2(120),
password VARCHAR2(120));
TYPE user_tab IS TABLE OF user_rec INDEX BY BINARY_INTEGER;
function Validate_logged_user (p_user_tab IN user_tab) RETURN VARCHAR2;
end Configuration_pkg;
create or replace package body Configuration_pkg as
function Validate_logged_user (p_user_tab IN user_tab) RETURN VARCHAR2 IS
Ismatching number;
begin
select count(1)
into Ismatching
from CG_M_USERS
where username = user_tab.email
and password = user_tab.password;
if Ismatching = 0 then
return 'Invalid username / password';
elsif Ismatching = 1 then
return 'Login successful';
end if;
end Validate_logged_user;
end Configuration_pkg;
I am getting the following error
Error(10,20): PL/SQL: ORA-00904: "USER_TAB"."PASSWORD": invalid identifier
Error(10,29): PLS-00302: component 'PASSWORD' must be declared
I want to validate the user with the value passed with the record type, not directly checking username and password from table. Everyone's help is highly appreciated.
There are few mistakes in your code.
1) You are using a Oracle reserve keyword 'PASSWORD'.
2) You are passing a collection to the function. So you need to run a loop to get the values of the collection.
See the revised complied code.
CREATE OR REPLACE PACKAGE Configuration_pkg
AS
TYPE user_rec IS RECORD
(
email VARCHAR2 (120),
passwrd VARCHAR2 (120)
);
TYPE user_tab IS TABLE OF user_rec INDEX BY BINARY_INTEGER;
FUNCTION Validate_logged_user (p_user_tab IN user_tab)
RETURN VARCHAR2;
END Configuration_pkg;
----------------------------------------
/
CREATE OR REPLACE PACKAGE BODY Configuration_pkg
IS
FUNCTION Validate_logged_user (p_user_tab IN user_tab)
RETURN VARCHAR2
IS
Ismatching NUMBER;
msg1 varchar2(20):= 'Invalid username/passwrd';
msg2 varchar2(20):= 'Login successful';
BEGIN
for r in 1..p_user_tab.count
loop
SELECT COUNT (1)
INTO Ismatching
FROM CG_M_USERS
WHERE username = p_user_tab(r).email
AND passwrd = p_user_tab(r).passwrd;
IF Ismatching = 0
THEN
RETURN msg1;
ELSIF Ismatching = 1
THEN
RETURN msg2;
END IF;
end loop;
END Validate_logged_user;
END Configuration_pkg;

Return NULL when no matching records are found

I'm passing an employee number to a function and getting back the Joined Date.
I want this to return NULL when no matching records found; in my case it is just returning blank or an empty row;
get_join_date(in_emp_no)
CREATE OR REPLACE FUNCTION get_join_date(in_emp_no) RETURN DATE IS
v_join_date DATE;
BEGIN
SELECT joined_date
INTO v_date
FROM employee
WHERE employee_number = in_emp_no
AND type = in_type;
IF v_join_date IS NOT NULL THEN
v_join_date := v_date;
ELSE
v_join_date = NULL;
END IF;
RETURN v_join_date;
END;
If no matching records are found then a NO_DATA_FOUND exception will be raised; you have to capture this exception and return something.
You're also checking whether v_join_date is null and then assigning a null value to it if it is null; there's no need to do this.
create or replace function get_join_date(
Pemp_no in number
) return date is
l_join_date date;
begin
select joined_date into l_join_date
from employee
where employee_number = Pemp_no
and type = in_type;
return l_join_date;
exception when no_data_found then
return null;
end;
Other errors include:
You're selecting into v_date but you declared your variable as v_join_date
There's no semi-colon after your end statement.
There's no semi-colon after your endif statement.
endif is two words, this should be end if;
You're not declaring the datatype of the parameter in_emp_no you need to (but not the length), for instance ... function get_join_date ( Pemp_no number ) ...
As you want to return either the one existing value or null, you can simply use an aggregate function, e.g. MIN, for that:
CREATE OR REPLACE FUNCTION get_join_date( in_emp_no) RETURN DATE
IS
v_join_DATE DATE;
BEGIN
SELECT MIN(JOINED_DATE)
INTO v_join_DATE
FROM employee
WHERE employee_number = in_emp_no
AND TYPE=in_type;
return v_join_DATE;
end

Resources