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;
/
Related
I am trying to make a select statement that selects the id user (employee or leader or manager) and full name, from a MySQL database. depending on the id dept , id priorty . The code is:
PROCEDURE `get_parent`(id_dept int, id_priorty int )
BEGIN
if(select employee_tb.id_dept from employee_tb where (employee_tb.id_dept=id_dept&&3=id_priorty))>0
then
begin
select id_employee ,full_name,3 as type from employee_tb;
end;
elseif(select leader_tb.id_dept from leader_tb where (leader_tb.id_dept=id_dept&&id_priorty=2))>0
then
begin
select id_leader, full_name, 2 as type from leader_tb;
end;
elseif(select manager_tb.id_dept from manager_tb where (manager_tb.id_dept=id_dept&&id_priorty=1))>0
then
begin
select id_manager, full_name, 1 as type from manager_tb;
end;
else
select "failed"as result ;
end if;
END
select id_employee ,full_name,3 as type from employee_tb;
select id_leader, full_name, 2 as type from leader_tb;
select id_manager, full_name, 1 as type from manager_tb;
those 3 lines in your code after checking condition selects all records from table anyway.
To fix this issue:
PROCEDURE `get_parent`(id_dept int, id_priorty int )
BEGIN
if(select employee_tb.id_dept from employee_tb where (employee_tb.id_dept=id_dept&&3=id_priorty))>0
then
begin
select id_employee ,full_name,3 as type from employee_tb where (employee_tb.id_dept=id_dept&&3=id_priorty);
end;
elseif(select leader_tb.id_dept from leader_tb where (leader_tb.id_dept=id_dept&&id_priorty=2))>0
then
begin
select id_leader, full_name, 2 as type from leader_tb where (leader_tb.id_dept=id_dept&&id_priorty=2);
end;
elseif(select manager_tb.id_dept from manager_tb where (manager_tb.id_dept=id_dept&&id_priorty=1))>0
then
begin
select id_manager, full_name, 1 as type from manager_tb where (manager_tb.id_dept=id_dept&&id_priorty=1);
end;
else
select "failed"as result ;
end if;
END
it will of course give one record if there is one record that matches this where clause.
The below is a function that I am creating to accept an array of varchar2 items and return the internal pk of that record which is a NUMBER for each record. I am struggling to get the syntax right to pass an array of type VARCHAR_ARRAY to the simple sql query in the cursor and return the variable of type NUMBER_ARRAY. the Error is on line 8,42 i.e FROM table_name WHERE column_name IN VARCHAR_ARRAY which was passed to the function. Please help me with this error as I am learning plsql.
create or replace TYPE VARCHAR_ARRAY AS VARRAY(1000000) OF VARCHAR2(1000);
/
create or replace TYPE NUMBER_ARRAY AS VARRAY(1000000) OF NUMBER;
/
create or replace Function GET_PRODUCT_ID_ARR(V_PRODUCT_NUM_ARR IN VARCHAR_ARRAY)
RETURN NUMBER_ARRAY
IS
product_id_list number_array := number_array();
CURSOR c1
IS
SELECT cat_map_id
FROM mn_cat_map WHERE product_num IN (V_PRODUCT_NUM_ARR) and catalog_type = 'INT';
v_output NUMBER;
BEGIN
OPEN c1;
LOOP
fetch c1 into product_id_list;
EXIT WHEN c1%notfound;
product_id_list.extend;
product_id_list(product_id_list.count) := v_output;
dbms_output.put_line('Product ('||v_output ||'):'||product_id_list(v_output));
END LOOP;
Close c1;
RETURN product_id_list;
END;
/
There is two issues:
You have to cast varray to table:
CURSOR c1
IS
SELECT cat_map_id
FROM mn_cat_map
WHERE product_num IN (select column_value from table(V_PRODUCT_NUM_ARR))
and catalog_type = 'INT';
Add bulk collect after fetch:
LOOP
fetch c1 bulk collect into product_id_list limit 100;
EXIT WHEN c1%notfound;
product_id_list.extend;
product_id_list(product_id_list.count) := v_output;
dbms_output.put_line('Product ('||v_output ||'):'||product_id_list(v_output));
END LOOP;
If you write limit 100, each loop will put 100 records in product_id_list. You can omit limit clause, in this case you will get all records in one fetch.
EDIT
How to see results:
declare
myarray varchar_array;
my_num_array number_array;
begin
myarray := varchar_array();
myarray.extend(2);
myarray(1) := '151043';
myarray(2) := '2895';
my_num_array := GET_PRODUCT_ID_ARR(myarray);
for i in 1 .. my_num_array.count loop
dbms_output.put_line(my_num_array(i));
end loop;
end;
/
What #William is said is quiet true. VARRAY(1000000) is not recommended. Inplace you can create a type of table. However if i follow what you have done, there seems some mistakes in your code. Please see below how you can do it.
Tables Preparation:
create table mn_cat_map(cat_map_id number,
product_num varchar2(1000),
catalog_type varchar2(10));
/
INSERT INTO T541682.MN_CAT_MAP (CAT_MAP_ID, PRODUCT_NUM, CATALOG_TYPE)
VALUES (10, 'A123', 'INT');
INSERT INTO T541682.MN_CAT_MAP (CAT_MAP_ID, PRODUCT_NUM, CATALOG_TYPE)
VALUES (2, 'B121', '2Wheer');
INSERT INTO T541682.MN_CAT_MAP (CAT_MAP_ID, PRODUCT_NUM, CATALOG_TYPE)
VALUES (3, 'C645', '4Wheer');
COMMIT;
create or replace TYPE VARCHAR_ARRAY AS VARRAY(1000000) OF VARCHAR2(1000);
/
create or replace TYPE NUMBER_ARRAY AS VARRAY(1000000) OF NUMBER;
/
Code: read explainatory comments inline
CREATE OR REPLACE FUNCTION GET_PRODUCT_ID_ARR (V_PRODUCT_NUM_ARR VARCHAR_ARRAY)
RETURN NUMBER_ARRAY
IS
product_id_list number_array := number_array ();
CURSOR c1(tbl_list VARCHAR_ARRAY)
IS
SELECT cat_map_id
FROM mn_cat_map
WHERE product_num in (select column_value from table(tbl_list)) ---Checking if the item exists in the table with passed collection
AND catalog_type = 'INT';
v_output NUMBER:= 0;
BEGIN
--not opening cursor and am not looking for processing any records.
--OPEN c1(V_PRODUCT_NUM_ARR);
--passing the input varray to the cursor.
for i in c1(V_PRODUCT_NUM_ARR)
loop
v_output:=v_output + 1;
product_id_list.extend;
product_id_list(product_id_list.COUNT):= i.cat_map_id;
DBMS_OUTPUT.put_line('Product (' || v_output || '):' ||product_id_list(product_id_list.COUNT));
end loop;
RETURN product_id_list;
END;
/
Execution:
SQL> select GET_PRODUCT_ID_ARR(VARCHAR_ARRAY('A123','B121','C645')) COl1 from dual;
COL1
--------------------------------------------------------------------------------
NUMBER_ARRAY(10)
Product (1):10
I am a beginner in plsql. The code below runs without any compilation error but the notFoundException is not getting called. Any help would be appriciated.
declare
abc exception;
notFoundException exception;
cursor c1(dd number) is select first_name from employees where salary = dd;
begin
for i in c1(&t)
loop
if(c1%rowcount!=1) then
raise abc;
elsif(c1%notfound) then
raise notFoundException;
else
dbms_output.put_line(i.first_name);
end if;
end loop;
Exception
when abc then
dbms_output.put_line('abc');
insert into messages values('too many rows exception');
when notFoundException then
dbms_output.put_line('notFoundException');
insert into messages values('Nobody with this salary : ');
end;
/
You may use the below anonymous block and try
`SET SERVEROUTPUT ON;
DECLARE
first_name_in employees.first_name%ROWTYPE;
Salary_in employees.salary%ROWTYPE;
BEGIN
Salary_in:=&Salary;
SELECT first_name
INTO first_name_in
FROM employees
WHERE salary = Salary_in;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('Nobody with this salary :'||Salary_in);
INSERT
INTO messages
VALUES ('NO_DATA_FOUND exception');
WHEN TOO_MANY_ROWS THEN
DBMS_OUTPUT.PUT_LINE('Too many with this salary :'||Salary_in);
INSERT
INTO messages
VALUES ('TOO_MANY_ROWS exception');
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('select failed with error'||SUBSTR(SQLERRM,1,100))
INSERT
INTO messages
VALUES (SUBSTR(SQLERRM,1,100));
COMMIT;
END;
/`
You don't need to use cursor attributes when using FOR LOOP. You could just query the records and make decision afterwards. Here is a running example (I didn't have the employees table, so made up a stub). The convenient thing about FOR LOOP is that you don't have to worry about closing the cursor.
DECLARE
abc EXCEPTION;
notfoundexception EXCEPTION;
CURSOR c1(dd NUMBER) IS
SELECT *
FROM (SELECT MOD(rownum, 3) salary
,rownum first_name
FROM dual
CONNECT BY LEVEL < 10)
WHERE salary = dd;
v_count NUMBER := 0;
BEGIN
FOR i IN c1(2) LOOP
v_count := v_count + 1;
IF (v_count > 1) THEN
RAISE abc;
END IF;
dbms_output.put_line(i.first_name);
END LOOP;
IF (v_count = 0) THEN
RAISE notfoundexception;
END IF;
EXCEPTION
WHEN abc THEN
dbms_output.put_line('abc');
--insert into messages values('too many rows exception');
WHEN notfoundexception THEN
dbms_output.put_line('notFoundException');
--insert into messages values('Nobody with this salary : ');
END;
/
I suggest you to read about cursors in PL/SQL.
A cursor for loop will loop through every record that is retrieved in the cursor. If no data is found, execution will never proceed inside the loop, hence no data found exception will not be raised inside the loop. Your code should be like this
declare
abc exception;
notFoundException exception;
lv_first_name varchar2(240);
cursor c1(dd number) is select first_name from employees where salary = dd;
begin
open c1;
fetch c1 into lv_first_name;
if(c1%rowcount > 1) then
raise abc;
elsif(c1%notfound) then
raise notFoundException;
else
dbms_output.put_line(lv_first_name);
end if;
close c1;
Exception
when abc then
dbms_output.put_line('abc');
insert into messages values('too many rows exception');
when notFoundException then
dbms_output.put_line('notFoundException');
insert into messages values('Nobody with this salary : ');
end;
I have the following stored procedure in MySQL
CREATE DEFINER=`test_db`#`%` PROCEDURE `ADD_ATTENDANCE`(IN `programID` INT, IN `clientID` INT, IN `insDate` DATETIME, IN `updDate` DATETIME, IN `insUser` INT, IN `updUser` INT, IN `lessonDate` DATE, IN `lessonTime` TIME)
LANGUAGE SQL
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT 'Add attedance to my calendar'
BEGIN
DECLARE max_sub, availability INT;
DECLARE cursor_max_sub CURSOR FOR SELECT max_sub FROM app_lesson WHERE id = programID;
DECLARE cursor_availability CURSOR FOR SELECT count(*) FROM attendance WHERE program_id = programID AND lesson_date = lessonDate AND lesson_time = lessonTime;
OPEN cursor_max_sub;
OPEN cursor_availability;
read_loop: LOOP
FETCH cursor_max_sub INTO max_sub;
FETCH cursor_availability INTO availability;
IF (availability < max_sub) THEN
insert into attendance (program_id, client_id, ins_date, upd_date, ins_user, upd_user, lesson_date, lesson_time)
values (programID, clientID, insDate, updDate, insUser, updUser, lessonDate, lessonTime);
LEAVE read_loop;
ELSE
insert into attendance_hold (program_id, client_id, ins_date, upd_date, ins_user, upd_user, lesson_date, lesson_time)
values (programID, clientID, insDate, updDate, insUser, updUser, lessonDate, lessonTime);
END IF;
END LOOP;
CLOSE cursor_max_sub;
CLOSE cursor_availability;
END;
Even though the cursor_max_sub is equal to 6 and the cursor_availability is equal to 4 my procedure always executes the else insert statement. Can you please help me out?
Thanks!!!
OK that was tricky... For some reason when i change the max_sub variable into maxNumberOfSubscription everything worked perfectly... Is max_sub some kind of reserved key word for MySQL or there was a complication because my variable had the same name with the returned field of select statement?
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;
/