SQL PL/SQL user defined multiple exception error handling (null) - exception

I'm trying to write a stored procedure that will have 2 exception errors.
Create table Employee
(emp_num varchar(10) primary key,
emp_name varchar(10),
DOB date,
job_title varchar(15),
marriage_date date,
spouseid varchar(10) references Employee(emp_num),
dept_id varchar(10) references Department(dept_id));
AS you see it's a table that will have employee info. There is a recursive relationship here that says that you need to provide spouse information if spouse is also employee in company (spouseid + marriage_date).
I created a stored procedure that asks for employee number and gives you spouseid, name, and marriage date.
CREATE OR REPLACE PROCEDURE DISP_SPOUSE
(SP_EMP_NUM IN EMPLOYEE.EMP_NUM%TYPE) AS
SP_NAME EMPLOYEE.EMP_NAME%TYPE;
SP_SPOUSEID EMPLOYEE.SPOUSEID%TYPE;
SP_MARRIAGE_DATE EMPLOYEE.MARRIAGE_DATE%TYPE;
BEGIN
SELECT SPOUSEID, EMP_NAME, MARRIAGE_DATE
INTO SP_SPOUSEID, SP_NAME, SP_MARRIAGE_DATE
FROM EMPLOYEE
WHERE EMP_NUM = SP_EMP_NUM;
DBMS_OUTPUT.PUT_LINE ((SP_SPOUSEID)||' '||
(SP_NAME)||' '||(SP_MARRIAGE_DATE));
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('NO EMPLOYEE WITH THAT NUMBER:'||SP_EMP_NUM);
END;
/
I also need to add a second user defined exception that will be raised if there is no spouseid. so i'm thinking something like this:
DECLARE
SP_EXCEPTION EXCEPTION;
PRAGMA EXCEPTION_INIT (SP_EXCEPTION, -20001);
BEGIN
RAISE_APPLICATION_ERROR (-20001, 'NO EMPLOYEE SPOUSE');
EXCEPTION
WHEN SP_EXCEPTION
THEN
DBMS_OUTPUT.PUT_LINE ( SQLERRM );
END;
/
And then add second exception to the stored procedure code (after or before no_data_found exception:
WHEN SPOUSEID IS NULL
RAISE SP_EXCEPTION;
end;
/
I'm having problem on the spouseid = null. is there a different way to write that? (some values that were inserted in spouseid are indeed null).

Nevermind I solved it.
CREATE OR REPLACE PROCEDURE DISP_SPOUSE
(SP_EMP_NUM IN EMPLOYEE.EMP_NUM%TYPE) AS
SP_NAME EMPLOYEE.EMP_NAME%TYPE;
SP_SPOUSEID EMPLOYEE.SPOUSEID%TYPE;
SP_MARRIAGE_DATE EMPLOYEE.MARRIAGE_DATE%TYPE;
SP_EXCEPTION EXCEPTION;
BEGIN
SELECT SPOUSEID, EMP_NAME, MARRIAGE_DATE
INTO SP_SPOUSEID, SP_NAME, SP_MARRIAGE_DATE
FROM EMPLOYEE
WHERE EMP_NUM = SP_EMP_NUM;
IF
SP_SPOUSEID IS NULL THEN
RAISE SP_EXCEPTION;
ELSE
DBMS_OUTPUT.PUT_LINE ((SP_SPOUSEID)||' '||
(SP_NAME)||' '||(SP_MARRIAGE_DATE));
END IF;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE
('NO EMPLOYEE WITH THAT NUMBER:'||SP_EMP_NUM);
WHEN SP_EXCEPTION THEN
DBMS_OUTPUT.PUT_LINE ('NO SPOUSE AS EMPLOYEE');
END;
/

Related

Specific errors messages for each input if not found in the database

I have developed the following function. I have been instructed that the function should return null if no teacher is found in the database. If the function returns null then it should output “no teacher found.” The code below accomplishes this. However, I would like to output specific responses if errors are made when keying in any of the 4 inputs. For instance, in addition to receiving the response “no teacher found” it would also say “subject not found” or “school not found.” Basically, if any of the 4 inputs was keyed in incorrectly, it would be clear as to the reason the function returned null. That way, if the a school name was misspelled by a single letter, the problem would be clear and evident instead of thinking that no teacher taught that particular class. How can I accomplish this?
CREATE OR REPLACE FUNCTION get_classroom_teacher(
subject_in IN subjects.subject%TYPE,
school_name_in IN schools.school_name%TYPE,
year_in IN classrooms.year%TYPE,
semester_in IN classrooms.semester%TYPE)
RETURN VARCHAR2
IS
l_teacher_name VARCHAR2(50);
BEGIN
SELECT first_name || ' ' || last_name
INTO l_teacher_name
FROM people
WHERE school_id IN (
SELECT school_id
FROM schools
WHERE school_name = school_name_in)
AND
person_id IN (
SELECT person_id
FROM teachers
WHERE subject_id IN (
SELECT subject_id
FROM subjects
WHERE subject = subject_in)
AND subject_id IN (
SELECT subject_id
FROM classrooms
WHERE semester = semester_in
AND year = year_in));
DBMS_OUTPUT.PUT_LINE(l_teacher_name);
RETURN l_teacher_name;
EXCEPTION
WHEN no_data_found THEN
RETURN NULL;
END get_classroom_teacher;
I call it like this:
DECLARE
l_subject subjects.subject%TYPE;
l_school schools.school_name%TYPE;
l_year classrooms.year%TYPE;
l_semester classrooms.semester%TYPE;
l_teacher_name VARCHAR2(60);
BEGIN
l_subject := 'Science';
l_school := 'Fayetteville-Manlius School';
l_year := 2021;
l_semester := 'spring';
l_teacher_name := get_classroom_teacher(l_subject, l_school, l_year, l_semester);
IF l_teacher_name IS NULL THEN
DBMS_OUTPUT.PUT_LINE('No teacher found.');
ELSE
DBMS_OUTPUT.PUT_LINE('The teacher is ' || l_teacher_name);
END IF;
EXCEPTION
WHEN OTHERS THEN
RAISE_APPLICATION_ERROR(-20001, 'An error was encountered - ' ||
SQLCODE ||
' -ERROR- ' ||
SQLERRM);
END;
The issue you face is that any of inputs may be the cause of a "No Data Found" exception. Further there could actually be multiple invalid parameters, i.e school and subject misspelled. To determine the exact cause then you must select each parameter independently. This could be a performance killer. Since we assume the majority of requests will be successful you can delay those selections until after the full query fails, then on a no data found exception do the individual selects. Since each individual then must also trap NO_DATA_FOUND you can create helper functions for them.
create or replace
function teacher_name(subject_in IN subjects.subject%TYPE
,school_name_in IN schools.school_name%TYPE
,year_in IN classrooms.year%TYPE
,semester_in IN classrooms.semester%TYPE
)
return varchar2
is
l_teacher varchar2(50);
l_school_id schools.school_id%TYPE;
l_subject_id subjects.subject_id%TYPE;
l_year classrooms.year%TYPE;
l_classroom classrooms.semester%TYPE;
procedure what_school() is
begin
select school_id
into l_school_id
from schools
where school_name = school_name_in)
exception
when no_data_found then l_school_id = null;
when too_many_rows then l_school_id = 0;
end what_school;
procedure what_subject() is
begin
select subject_id
into l_subject_id
from subjects
where subject_name = subject_name_in)
exception
when no_data_found then null;
when too_many_rows then subject_id = 0;
end what_subject;
-- Functions for semester, year ...
begin
<< Your query here >>
return the_teacher;
exception
when no_data_found then
l_school_id := when_school;
l_subject_id := what_subject;
l_year := what_year;
l_semester := what_semester;
if l_school_id is null then
dbms_output.put_line( ' No such school as: ' || school_name_in;
if l_subject_id is null then
dbms_output.put_line( ' No such subject as: ' || subject_in;
if l_year is null then
dbms_output.put_line( ' No such yeear as: ' || year_in;
if l_semester is null then
dbms_output.put_line( ' No such semester as: ' || semester_id;
if l_school_id is not null
and l_subject_id is not null
and l_year is not null
and l_semester is not null
then
dbms_output.put_line( ' No Date Found')
return null;
end if;
end teacher_name;
NOTE: No sample data nor table DDL; Not Tested.
We can define bespoke exceptions and raise them in procedures. EXCEPTION is a PL/SQL data type, and we can associate an error number from the range set aside for user-defined exceptions (-20999 to -20000) using the exception_init pragma. So your function might look like this:
CREATE OR REPLACE FUNCTION get_classroom_teacher(
subject_in IN subjects.subject%TYPE,
school_name_in IN schools.school_name%TYPE,
year_in IN classrooms.year%TYPE,
semester_in IN classrooms.semester%TYPE)
RETURN VARCHAR2
IS
l_teacher_name VARCHAR2(50);
x_subject_null exception;
pragma exception_init (x_subject_null, -20000);
x_school_name_null exception;
pragma exception_init (x_school_name_null, -20001);
x_semester_null exception;
pragma exception_init (x_semester_null, -20002);
x_year_null exception;
pragma exception_init (x_year_null, -20003);
BEGIN
if subject_in is null then
raise x_subject_null;
elsif school_name_in is null then
raise x_school_name_null;
elsif subject_in is null then
raise x_semester_null;
elsif year_in is null then
raise x_year_null;
end if;
SELECT first_name || ' ' || last_name
INTO l_teacher_name
FROM people
WHERE school_id IN (
SELECT school_id
FROM schools
WHERE school_name = school_name_in)
AND
person_id IN (
SELECT person_id
FROM teachers
WHERE subject_id IN (
SELECT subject_id
FROM subjects
WHERE subject = subject_in)
AND subject_id IN (
SELECT subject_id
FROM classrooms
WHERE semester = semester_in
AND year = year_in));
DBMS_OUTPUT.PUT_LINE(l_teacher_name);
RETURN l_teacher_name;
EXCEPTION
WHEN no_data_found THEN
RETURN NULL;
END get_classroom_teacher;
The catch is those user-defined exceptions are only useful within the scope of the function. They are no different from any other variable defined in a standalone program unit. Consequently, your calling block cannot reference them, which means you cannot log a bespoke error message for each exception.
The solution is to define the exceptions in a package spec which can be referenced by both the function and the anonymous block.
create or replace package ude as
x_subject_null exception;
pragma exception_init (x_subject_null, -20000);
x_school_name_null exception;
pragma exception_init (x_school_name_null, -20001);
x_semester_null exception;
pragma exception_init (x_semester_null, -20002);
x_year_null exception;
pragma exception_init (x_year_null, -20003);
end;
/
Reference the package in your function ....
CREATE OR REPLACE FUNCTION get_classroom_teacher(
subject_in IN subjects.subject%TYPE,
school_name_in IN schools.school_name%TYPE,
year_in IN classrooms.year%TYPE,
semester_in IN classrooms.semester%TYPE)
RETURN VARCHAR2
IS
l_teacher_name VARCHAR2(50);
BEGIN
if subject_in is null then
raise ude.x_subject_null;
elsif school_name_in is null then
raise ude.x_school_name_null;
elsif subject_in is null then
raise ude.x_semester_null;
elsif year_in is null then
raise ude.x_year_null;
end if;
SELECT first_name || ' ' || last_name
INTO l_teacher_name
FROM people
WHERE school_id IN (
SELECT school_id
FROM schools
WHERE school_name = school_name_in)
AND
person_id IN (
SELECT person_id
FROM teachers
WHERE subject_id IN (
SELECT subject_id
FROM subjects
WHERE subject = subject_in)
AND subject_id IN (
SELECT subject_id
FROM classrooms
WHERE semester = semester_in
AND year = year_in));
DBMS_OUTPUT.PUT_LINE(l_teacher_name);
RETURN l_teacher_name;
EXCEPTION
WHEN no_data_found THEN
RETURN NULL;
END get_classroom_teacher;
and in your calling block ....
DECLARE
l_subject subjects.subject%TYPE;
l_school schools.school_name%TYPE;
l_year classrooms.year%TYPE;
l_semester classrooms.semester%TYPE;
l_teacher_name VARCHAR2(60);
BEGIN
l_subject := 'Science';
l_school := 'Fayetteville-Manlius School';
l_year := 2021;
l_semester := 'spring';
l_teacher_name := get_classroom_teacher(l_subject, l_school, l_year, l_semester);
IF l_teacher_name IS NULL THEN
DBMS_OUTPUT.PUT_LINE('No teacher found.');
ELSE
DBMS_OUTPUT.PUT_LINE('The teacher is ' || l_teacher_name);
END IF;
EXCEPTION
when ude.x_subject_null then
dbms_output.put_line ('Subject parameter was null');
raise;
when ude.x_school_name_null then
dbms_output.put_line ('School name parameter was null');
raise
when ude.x_semester_null then
dbms_output.put_line ('Semester parameter was null');
raise;
when ude.x_year_null then
dbms_output.put_line ('Year parameter was null');
raise;
WHEN OTHERS THEN
RAISE_APPLICATION_ERROR(-20001, 'An error was encountered - ' ||
SQLCODE ||
' -ERROR- ' ||
SQLERRM);
END;
You may choose not to re-raise those handled exceptions.
Generally speaking, a WHEN OTHERS branch like that is considered bad practice. You're suppressing some useful information without getting anything in return. WHEN OTHERS is useful when we want to take some action for all exceptions, such as writing a message to a log table. But just re-raising every error with a single ERRNO is pointless.

Handle exceptions on procedures PL/SQL

Hi I would like to handle exceptions on my procedures to make it more useful.
My procedure adding occupations and it's wage, here is table:
CREATE TABLE Occupations(
id_oc NUMBER(2),
Name_oc VARCHAR2(20) CONSTRAINT OC_name_cc NOT NULL ,
Min_wage NUMBER(7,2) CONSTRAINT OC_min_wg NOT NULL CHECK(Min_wage>100),
CONSTRAINT Oc_id_pk PRIMARY KEY (id_oc),
CONSTRAINT OC_na UNIQUE (Name_oc)
);
Here is the procedure
create or replace procedure zad_2a(name varchar2, min_wage number)
is
isFound occupations.name_oc%TYPE;
cursor c1
is
select name_oc from occupations where name_oc = name;
begin
open c1;
fetch c1 into isFound;
if c1%found then dbms_output.put_line('Occupation already exists');
else insert into occupations values(Seq_Occupations.NEXTVAL, name, min_wage);
end if;
close c1;
end;
/
This procedure should have following exceptions:
When I set wage fewer than 100 and more than 5000
While I add bad occupation name - using numbers instead of characters
handling other unexcepted errors
How to do this?
You can try as below and modify as per your requirements.
CREATE OR REPLACE PROCEDURE zad_2a (ename VARCHAR2, min_wage NUMBER)
IS
isfound occupations.name_oc%TYPE;
e_custom_exception EXCEPTION;
PRAGMA EXCEPTION_INIT (e_custom_exception, -20001);
l_count_int NUMBER;
CURSOR c1
IS
SELECT name_oc
FROM occupations
WHERE name_oc = ename;
BEGIN
IF (min_wage < 100 OR min_wage > 5000)
THEN
RAISE e_custom_exception;
END IF;
SELECT COUNT (*)
INTO l_count_int
FROM DUAL
WHERE REGEXP_LIKE (ename, '[[:digit:]]');
IF (l_count_int > 0)
THEN
RAISE e_custom_exception;
END IF;
OPEN c1;
FETCH c1 INTO isfound;
IF c1%FOUND
THEN
DBMS_OUTPUT.put_line ('Occupation already exists');
ELSE
INSERT INTO occupations
VALUES (Seq_Occupations.NEXTVAL, ename, min_wage);
END IF;
CLOSE c1;
EXCEPTION
WHEN e_custom_exception
THEN
DBMS_OUTPUT.put_line (SQLERRM);
WHEN OTHERS
THEN
RAISE;
END;
/

I need some assistance in putting an IF statement in this PLSQL function

I need to put in IF statement which returns a specific student id "999999" when the current student cannot be found by first and last name. Here is what I am currently working with:
BEGIN
DECLARE ret_student_id INT;
SELECT
student_id
INTO ret_student_id FROM
students
WHERE
first_name LIKE var_first_name
AND last_name LIKE var_last_name;
RETURN ret_student_id;
END
Just set it to the value before your select.
The select does not update your value if there is nothing in the select.
BEGIN
DECLARE ret_student_id INT;
SET ret_student_id=999999;
SELECT
student_id
INTO ret_student_id FROM
students
WHERE
first_name LIKE var_first_name
AND last_name LIKE var_last_name;
RETURN ret_student_id;
END
This SELECT statement raises a NO_DATA_FOUND exception when not exactly one row is found.
BEGIN
SELECT ...
EXCEPTION
WHEN NO_DATA_FOUND THEN
RETURN 999999;
END;

MySql Function Creation Error

I write a function like the following. the purpose of this function is to return the place of a student by some specific exam in a branch.
DELIMITER $$
CREATE FUNCTION `getMerit`( branch VARCHAR(50), totalMark DECIMAL(19,2), comaSeparetedExamIds VARCHAR(200) ) RETURNS INT(11)
BEGIN
SET #comaSeparetedExamIds=comaSeparetedExamIds;
SET #branch =branch;
SET #marks=totalMark;
SELECT #place=COUNT(*)+1
FROM (
SELECT SUM(m.marks) marks
FROM marksheet m, studentinfo s
WHERE exam_id IN (#comaSeparetedExamIds)
AND m.student_roll=s.roll_no
AND s.branch LIKE CONCAT(#branch,'%')
GROUP BY m.student_roll
) AS a
WHERE a.marks>#totalMark;
RETURN #place;
END$$
DELIMITER ;
But It shows me an error. the Error is
Query : CREATE FUNCTION getMerit( branch varchar(50), totalMark
DECIMAL(19,2), comaSeparetedExamIds varchar(200) ) RETURNS int(11)
BEG... Error Code : 1415 Not allowed to return a result set from a
function
What mistake I made here, Can anyone please help me?
You can't name input variables with #. # is used for user variables, ie connection local variables that don't needs to be declared.
Also you can't have selects in functions.
Procedures can return result sets but return values.
Functions can return values but not result sets.
They also differs in how you use them.
select function_name(1) from dual;
select id, name, funcation_name(id, name) from anyTable;
call procedure_name(1);
And when assigning variables inside selects you need to do := and not =. In your code you are actually selecting true or false and not the count.
This should work.
DELIMITER $$
CREATE FUNCTION `getMerit`( branch VARCHAR(50), totalMark DECIMAL(19,2), comaSeparetedExamIds VARCHAR(200) ) RETURNS INT(11)
BEGIN
SET #comaSeparetedExamIds=comaSeparetedExamIds;
SET #branch =branch;
SET #marks=totalMark;
SELECT COUNT(*)+1 INTO #place
FROM (
SELECT SUM(m.marks) marks
FROM marksheet m, studentinfo s
WHERE exam_id IN (#comaSeparetedExamIds)
AND m.student_roll=s.roll_no
AND s.branch LIKE CONCAT(#branch,'%')
GROUP BY m.student_roll
) AS a
WHERE a.marks>#totalMark;
RETURN #place;
END$$
DELIMITER ;

test a stored procedure in MySql Workbench

I have an Insert stored procedure where I am inserting into 2 tables. The second table using the Last_Insert_ID of the first table. Here is my sproc:
DELIMITER $$
CREATE DEFINER=`root`#`%` PROCEDURE `new_user_create`(
IN oFarmName varchar(45),
IN oFirstName varchar(45),
IN oAddress1 varchar(45),
IN oCity varchar(45),
IN oState varchar(45),
IN oZip varchar(45),
IN oCountry varchar(45)
)
BEGIN
insert into intelliair.individual
( FarmName, FirstName)
values ( oFarmName, oFirstName);
insert into intelliair.address
(IndividualID, Address1, City, State, Zip, Country)
Values (Last_Insert_ID(), oAddress1, oCity, oState, oZip, oCountry);
END
Here is how I am testing the query in MySql workbench:
call new_user_create(#myFarm, #MyName, #MyAddress, #MyCity, #MyState, #MyZip, #MyCountry)
There error I get is: "Column Address1 cannot be null"
Where am I going wronng? Is it in the sproc? Or the way I am calling it?
"Column Address1 cannot be null" indicates that the intelliair.address.Address1 field must be defined not null.
And, I don't think that you pre defined value for #MyAddress before passing it to the stored procedure.
Unless defined it is treated as NULL and hence is the error thrown.
To cross check values before calling the stored procedure like :
select #MyAddress; -- ,#myFarm, #MyName, #MyCity, #MyState, #MyZip, #MyCountry;
Update 1:
You can call stored procedure by directly inputting values for each of the parameters.
Example:
call new_user_create(
'my Farm value', -- #myFarm
'E B', -- #MyName
'My Address is SO', -- #MyAddress1
'My City is Coders', -- #MyCity
'CA', -- #MyState
'12345', -- #MyZip
'US' -- #MyCountry
);
The exception is being thrown by an INSERT (or UPDATE) statement, that is assigning a NULL value to a column named Address1 that is declared to be NOT NULL.
The most likely explanation, from what you show, is that the value passed in as the oAddress1 parameter is NULL, and the exception is being thrown by the second INSERT statement.
The most likely explanation, therefore, is that when the call to the procedure is made, the #MyAddress user variable is not assigned a value.
(You can verify there is not an Address1 column on the intelliair.individual table, or that if there is one, it's not defined as NOT NULL.)
(There's also a possibility that it's not one of your statements that's throwing the exception, but rather a recursive SQL statement, like an INSERT statement in a BEFORE INSERT FOR EACH ROW trigger.)
USE lportal;
DELIMITER $$
CREATE PROCEDURE `iemp`(IN eid INT(11),IN ename varchar(15),IN dname varchar(15),IN doj DATE)
BEGIN
INSERT INTO e_emp (eid,ename,dname,doj) VALUES (eid,ename,dname,doj);
END