PL/SQL Procedure, raise no_data_found during insert - sql

So here is the code as given to me in my oracle class:
CREATE OR REPLACE PROCEDURE add_job_hist
(p_emp_id IN employees.employee_id%TYPE,
p_new_jobid IN jobs.job_id%TYPE) IS
BEGIN
INSERT INTO job_history
SELECT employee_id, hire_date, SYSDATE, job_id, department_id
FROM employees
WHERE employee_id = p_emp_id;
UPDATE employees
SET hire_date = SYSDATE,
job_id = p_new_jobid,
salary = (SELECT min_salary + 500
FROM jobs
WHERE job_id = p_new_jobid)
WHERE employee_id = p_emp_id;
DBMS_OUTPUT.PUT_LINE('Added employee ' || p_emp_id || '
details to the JOB_HISTORY table');
DBMS_OUTPUT.PUT_LINE('Updated current job of employee ' ||
p_emp_id|| 'to ' || p_new_jobid);
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE_APPLICATION_ERROR (-20001, 'Employee does not exist!');
END add_job_hist;
/
The problem is that this is not a "SELECT INTO" statement, so the WHEN NO_DATA_FOUND exception isn't processed. I can raise my own user-defined exception, but it doesn't seem to let me do this in a procedure. How can I fix this so that it handles the exception? Because right now, if i give a p_emp_id that does not exist, it handles it anyway and the instructions do not want any additional procedure variables.
Thanks!

Check using SQL%ROWCOUNT after the insert statement, if it returns 0, then raise no_data_found explicitly.
CREATE OR REPLACE PROCEDURE add_job_hist(p_emp_id IN employees.employee_id%TYPE,
p_new_jobid IN jobs.job_id%TYPE) IS
BEGIN
INSERT INTO job_history
SELECT employee_id,
hire_date,
SYSDATE,
job_id,
department_id
FROM employees
WHERE employee_id = p_emp_id;
IF SQL%ROWCOUNT = 0 THEN
RAISE no_data_found;
END IF;
UPDATE employees
SET hire_date = SYSDATE,
job_id = p_new_jobid,
salary =
(SELECT min_salary + 500 FROM jobs WHERE job_id = p_new_jobid)
WHERE employee_id = p_emp_id;
dbms_output.put_line('Added employee ' || p_emp_id || '
details to the JOB_HISTORY table');
dbms_output.put_line('Updated current job of employee ' || p_emp_id || 'to ' || p_new_jobid);
EXCEPTION
WHEN no_data_found THEN
raise_application_error(-20001, 'Employee does not exist!');
END add_job_hist;
/

Related

Create a procedure to give bonuses to employees

I'm a beginner of PL/SQL.
I need to create a procedure.
The procedure name is sp_emp_bonus that takes 3 parameters:
department_id
job_id
amount
The procedure will update of bonus of employees within the department_id and job_id and increase existing bonus by the amount.
After the update statement, print using DBMS_OUTPUT.PUT_LINE to print out the number of employees affected by update (hint use implicit cursor attribute).
If no employees found then an exception section should capture NO_DATA_FOUND and print employees not found. Also include error handling for WHEN OTHERS ... and print DBMS_OUTPUT.PUT_LINE(SUBSTR(SQLERRM,1,100));
How to write this?
You can use simple update statement and sql%rowcount in procedure as follows:
CREATE OR REPLACE PROCEDURE SP_EMP_BONUS (
P_DEPARTMENT_ID IN NUMBER,
P_JOB_ID IN NUMBER,
P_AMOUNT IN NUMBER
) AS
LV_UPDATED_COUNT NUMBER := 0;
BEGIN
UPDATE YOUR_TABLE
SET
SALARY = SALARY + AMOUNT
WHERE DEPARTMENT_ID = P_DEPARTMENT_ID
AND JOB_ID = P_JOB_ID;
LV_UPDATED_COUNT := SQL%ROWCOUNT;
IF LV_UPDATED_COUNT = 0 THEN
DBMS_OUTPUT.PUT_LINE('no records found');
ELSE
DBMS_OUTPUT.PUT_LINE('Number of records updated: ' || LV_UPDATED_COUNT);
END IF;
END SP_EMP_BONUS;
/
Hope this will answer your question:
create or replace procedure sp_emp_bonus(
ip_dep_id in departments.department_id%type,
ip_job_id in jobs.job_id%type,
amount in number
)
is
no_emp_found exception;
pragma exception_init(no_emp_found, -20101);
cnt integer := 0;
begin
update
employees e
set e.salary = e.salary + nvl(amount, 0)
where
e.department_id = ip_dep_id and
e.job_id = ip_job_id;
cnt := sql%rowcount;
if cnt = 0 then
raise no_emp_found;
else
dbms_output.put_line('Employees updated ' || cnt);
end if;
commit;
exception
when no_emp_found then
rollback;
dbms_output.put_line('No Employees found for given Department and Job');
when OTHERS then
rollback;
DBMS_OUTPUT.PUT_LINE(SUBSTR(SQLERRM,1,100));
end;
Thanks.

The when others then does not work. However, the first exception handling works if the department name does not exist

I have tried to solve this problem by adding when others then but it does not seem to work. It only works when the department name does not exist, it would show No DATA FOUND but when the department name matches, it does not show anything. I want to know how I can fix this issue
DECLARE
dept_id departments.department_id%TYPE;
mgr_id departments.manager_id%TYPE;
BEGIN
SELECT department_ID, Manager_ID
INTO dept_id, mgr_id
FROM departments
Where Department_name = 'IT';
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('NO DATA FOUND');
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE ('The department ID is: ' || dept_id ||
'; Manager ID is: ' || mgr_id);
-- change the value of IT to 'ABC'
END;
I have to use when others for this problem
The data will be selected and will be available to use just after the select statement.When the select statement fails the control jumps to exception block. User can handle exception as desired. Purpose of when others is to handle any Exception that has not been handled, primary used when developer doesn't have any control and wan't to handle ANY exception. For example TOO_MANY_ROWS is not handled in the code but will get caught in the OTHERS Exception.
DECLARE
dept_id departments.department_id%TYPE;
mgr_id departments.manager_id%TYPE;
BEGIN
SELECT department_ID, Manager_ID
INTO dept_id, mgr_id
FROM departments
Where Department_name = 'IT';
DBMS_OUTPUT.PUT_LINE ('The department ID is: ' || dept_id ||
'; Manager ID is: ' || mgr_id);
IF dept_id IS NOT NULL THEN
UPDATE departments
SET DEPARTMENT_NAME ='ABC'
WHERE department_ID = dept_id
AND Manager_ID = mgr_id;
END IF;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('NO DATA FOUND');
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE (' In other Error '||SQLERRM||' '||SQLCODE);
-- change the value of IT to 'ABC'
END;

Oracle: How to wrap a Cursor inside A Function?

I am working on an assignment and it requires me to place a cursor inside a function.
Now my cursor works fine, but I am having a hard time placing it inside a a function, how can I do this?
The function is supposed to have the same argument as the cursor.
Code:
DECLARE
CURSOR c_emp_salary (v_job_title VARCHAR2)
IS
SELECT employee_id, first_name, salary, min_salary FROM employees e JOIN jobs j ON
e.job_id = j.job_id
AND j.job_title = v_job_title;
v_emp_id employees.employee_id%TYPE;
v_emp_first_name employees.first_name%TYPE;
v_emp_salary employees.salary%TYPE;
v_min_salary jobs.min_salary%TYPE;
BEGIN
OPEN c_emp_salary('Shipping Clerk'); --I WANT THIS VALUE TO BE FROM A FUNCTION.
LOOP
FETCH c_emp_salary INTO v_emp_id, v_emp_first_name, v_emp_salary, v_min_salary;
EXIT WHEN c_emp_salary%NOTFOUND;
dbms_output.put_line('Employee ID: ' || v_emp_id);
dbms_output.put_line('Employee First Name: ' || v_emp_first_name);
dbms_output.put_line('Employee Salary: ' || v_emp_salary);
dbms_output.put_line('Job Min Salary: ' || v_min_salary);
dbms_output.put('Result? ');
IF v_emp_salary = v_min_salary THEN dbms_output.put('Yes');
ELSE dbms_output.put('No');
END IF;
dbms_output.put_line(NULL);
dbms_output.put_line('++++++++++++++++++++++++');
END LOOP;
END;
The assignment description: Write a function that takes job_title and checks all employees in
that job, if an employee has salary equal to min_salary print
“yes” else print “no”. Use a cursor
"Use a cursor" is pointless because every DML or SELECT statement is a cursor.
Anyway, I assume you are looking for this:
CREATE OR REPLACE FUNCTION WRITE_SALARY(v_job_title IN VARCHAR2) AS
CURSOR c_emp_salary IS
SELECT employee_id, first_name, salary, min_salary
FROM employees e
JOIN jobs j ON e.job_id = j.job_id
WHERE j.job_title = v_job_title;
v_emp_id employees.employee_id%TYPE;
v_emp_first_name employees.first_name%TYPE;
v_emp_salary employees.salary%TYPE;
v_min_salary jobs.min_salary%TYPE;
BEGIN
OPEN c_emp_salary;
...
I assume you need something as:
(It needed substitute SCHEMA_NAME and FUNCTION_NAME)
(If return does not needed procedure can be used, and return statements should be removed)
(I don't run this example)
CREATE OR REPLACE function SCHEMA_NAME.FUNCTION_NAME( v_job_title VARCHAR2) return number is
CURSOR c_emp_salary
IS
SELECT employee_id, first_name, salary, min_salary FROM employees e JOIN jobs j ON
e.job_id = j.job_id
AND j.job_title = v_job_title;
v_emp_id employees.employee_id%TYPE;
v_emp_first_name employees.first_name%TYPE;
v_emp_salary employees.salary%TYPE;
v_min_salary jobs.min_salary%TYPE;
result number;
BEGIN
OPEN c_emp_salary('Shipping Clerk'); --I WANT THIS VALUE TO BE FROM A FUNCTION.
LOOP
FETCH c_emp_salary INTO v_emp_id, v_emp_first_name, v_emp_salary, v_min_salary;
EXIT WHEN c_emp_salary%NOTFOUND;
dbms_output.put_line('Employee ID: ' || v_emp_id);
dbms_output.put_line('Employee First Name: ' || v_emp_first_name);
dbms_output.put_line('Employee Salary: ' || v_emp_salary);
dbms_output.put_line('Job Min Salary: ' || v_min_salary);
dbms_output.put('Result? ');
IF v_emp_salary = v_min_salary THEN
dbms_output.put('Yes');
result := 1;
ELSE dbms_output.put('No');
result := 0;
END IF;
dbms_output.put_line(NULL);
dbms_output.put_line('++++++++++++++++++++++++');
return result;
END LOOP;
END;
The OP originally asked:
I am having a hard time placing [the cursor] inside a a function, how can I do this?
and
OPEN c_emp_salary('Shipping Clerk'); --I WANT THIS VALUE TO BE FROM A FUNCTION.
The code below solves the problem originally asked in the question; it is not intended to solve the assignment question that was edited in later as they are not one-and-the-same (that is left as an exercise to the OP).
Oracle Setup:
CREATE TABLE employees (
employee_id NUMBER,
first_name VARCHAR2(50),
salary NUMBER(10,2),
job_id NUMBER
);
CREATE TABLE jobs (
job_id NUMBER,
job_title VARCHAR2(50),
min_salary NUMBER(10,2)
);
INSERT INTO employees VALUES ( 1, 'Alice', 300, 1 );
INSERT INTO jobs VALUES ( 1, 'Shipping Clerk', 200 );
PL/SQL Block:
DECLARE
v_emp_id employees.employee_id%TYPE;
v_emp_first_name employees.first_name%TYPE;
v_emp_salary employees.salary%TYPE;
v_min_salary jobs.min_salary%TYPE;
c_emp_cursor SYS_REFCURSOR;
FUNCTION get_cursor(
v_job_title IN JOBS.JOB_TITLE%TYPE
) RETURN SYS_REFCURSOR
IS
c_emp_salary SYS_REFCURSOR;
BEGIN
OPEN c_emp_salary FOR
SELECT employee_id,
first_name,
salary,
min_salary
FROM employees e
JOIN jobs j
ON ( e.job_id = j.job_id
AND j.job_title = v_job_title );
RETURN c_emp_salary;
END;
BEGIN
c_emp_cursor := get_cursor('Shipping Clerk');
LOOP
FETCH c_emp_cursor INTO v_emp_id, v_emp_first_name, v_emp_salary, v_min_salary;
EXIT WHEN c_emp_cursor%NOTFOUND;
dbms_output.put_line('Employee ID: ' || v_emp_id);
dbms_output.put_line('Employee First Name: ' || v_emp_first_name);
dbms_output.put_line('Employee Salary: ' || v_emp_salary);
dbms_output.put_line('Job Min Salary: ' || v_min_salary);
dbms_output.put('Result? ');
IF v_emp_salary = v_min_salary THEN dbms_output.put('Yes');
ELSE dbms_output.put('No');
END IF;
dbms_output.put_line(NULL);
dbms_output.put_line('++++++++++++++++++++++++');
END LOOP;
END;
/
Output:
Employee ID: 1
Employee First Name: Alice
Employee Salary: 300
Job Min Salary: 200
Result? No
++++++++++++++++++++++++
db<>fiddle here

How to call a function within a procedure to update all records of a table on PLSQL?

I'm practicing PLSQL and I'm coding a package with 2 functions to update commission and the other one to update salary but now I want to create a procedure within the same package to update commission and salary for all employees using the functions on the package. Is it possible?
CREATE OR REPLACE PACKAGE BODY emp_upd_pkg IS
-- Function to update commission_pct --
FUNCTION comm_upd(
p_empid employees.commission_pct%TYPE)
RETURN NUMBER
IS
v_new_comm employees.commission_pct%TYPE;
BEGIN
UPDATE employees
SET commission_pct = commission_pct * 1.1
WHERE employee_id = p_empid;
SELECT commission_pct
INTO v_new_comm
FROM employees
WHERE employee_id = p_empid;
RETURN v_new_comm;
EXCEPTION
WHEN
NO_DATA_FOUND THEN
RAISE_APPLICATION_ERROR(-20992, 'NO EXISTE EMPLEADO');
END comm_upd;
-- Function to update salary --
FUNCTION sal_upd(
p_empid employees.salary%TYPE)
RETURN employees.salary%TYPE
IS
v_newsal employees.salary%TYPE;
BEGIN
UPDATE employees
SET salary = salary + 350
WHERE employee_id = p_empid;
-- Consulta select para la salida del a funcion --
SELECT salary
INTO v_newsal
FROM employees
WHERE employee_id = p_empid;
RETURN v_newsal;
END sal_upd;
-- Procedure to update all records of employees table --
PROCEDURE comm_sal_upd(
p_new_comm employees.commission_pct%TYPE,
p_new_sal employees.salary%TYPE);
END emp_upd_pkg;
I've tried creating a cursor and fetching into functions but I didn't succeed.
PROCEDURE comm_sal_upd(
p_new_comm employees.commission_pct%TYPE,
p_new_sal employees.salary%TYPE)
IS
CURSOR emp_cur IS
SELECT commission_pct, salary
FROM employees;
BEGIN
OPEN emp_cur;
FETCH emp_cur
INTO emp_upd_pkg.comm_upd(p_comm), emp_upd_pkg.sal_upd(p_sal);
CLOSE emp_cur;
END comm_sal_upd;
You are using function so its returing a value. you must capture the value in your procedure as below:
PROCEDURE comm_sal_upd(
p_new_comm employees.commission_pct%TYPE,
p_new_sal employees.salary%TYPE)
IS
CURSOR emp_cur IS
SELECT commission_pct, salary
FROM employees;
var number;
var2 employees.salary%TYPE;
BEGIN
for rec in emp_cur
loop
var:= emp_upd_pkg.comm_upd(p_comm);
var2:=emp_upd_pkg.sal_upd(p_sal);
dbms_output.put_line('updated commission--'|| var || ' Updated Sal -- '|| var2);
end loop;
commit;
END comm_sal_upd;
First of all, few tricks to improve your functions.
You don't need to make two queries when you can make one. Instead of
v_new_comm employees.commission_pct%TYPE;
BEGIN
UPDATE employees
SET commission_pct = commission_pct * 1.1
WHERE employee_id = p_empid;
SELECT commission_pct
INTO v_new_comm
FROM employees
WHERE employee_id = p_empid;
RETURN v_new_comm;
use returning clause:
v_new_comm employees.commission_pct%TYPE;
BEGIN
UPDATE employees
SET commission_pct = commission_pct * 1.1
WHERE employee_id = p_empid
returning commission_pct
into v_new_comm;
RETURN v_new_comm;
Also, this exception block doesn't look like very helpful:
EXCEPTION
WHEN
NO_DATA_FOUND THEN
RAISE_APPLICATION_ERROR(-20992, 'NO EXISTE EMPLEADO');
You just change the language of the error message.
As for the procedure to update all records, there are several issues:
You should avoid update many records one by one, it significantly reduces the performance. If possible, update them all together:
UPDATE employees
SET commission_pct = commission_pct * 1.1,
salary = salary + 350;
Cursors don't work this way. You shold use them to select data.
To process all rows in cursors, use loops:
PROCEDURE comm_sal_upd(
p_new_comm employees.commission_pct%TYPE,
p_new_sal employees.salary%TYPE)
IS
CURSOR emp_cur IS
SELECT commission_pct, salary
FROM employees;
v_comission number;
v_salary number;
BEGIN
OPEN emp_cur;
loop
FETCH emp_cur INTO v_comission, v_salary;
EXIT WHEN emp_cur%NOTFOUND;
<do something>
end loop;
CLOSE emp_cur;
END comm_sal_upd;
With one FETCH you process only one record.
I think the following is the kind of thing you are trying to do. (Untested, probably has bugs.) Note this is the least efficient way to do anything in PL/SQL, so it's just to demonstrate how you might structure procedures that call each other.
(btw I don't know if there is some textbook out there that tells people to code in uppercase, but there is really no need to.)
create or replace package body emp_upd_pkg
as
-- Update commission_pct for one employee:
procedure comm_upd
( p_empid employees.employee_id%type
, p_new_comm employees.commission_pct%type )
is
begin
update employees set commission_pct = commission_pct * p_new_comm
where employee_id = p_empid;
if sql%rowcount = 0 then
raise_application_error(-20992, 'Commission update failed: employee id ' || p_empid || ' not found.', false);
end if;
end comm_upd;
-- Update salary for one employee:
procedure sal_upd
( p_empid employees.employee_id%type
, p_new_sal employees.salary%type )
is
begin
update employees set salary = salary + p_new_sal
where employee_id = p_empid;
if sql%rowcount = 0 then
raise_application_error(-20993, 'Salary update failed: employee id ' || p_empid || ' not found.', false);
end if;
end sal_upd;
-- Update all employees:
procedure comm_sal_upd
( p_new_comm employees.commission_pct%type
, p_new_sal employees.salary%type )
is
begin
for r in (
select employee_id from employees
)
loop
comm_upd(r.employee_id, p_new_comm);
sal_upd(r.employee_id, p_new_sal);
end loop;
end comm_sal_upd;
end emp_upd_pkg;

How I can resolve this Procedure in Oracle PLSQL (It's not homework or something like that)

I'm learning Oracle Database and PL/SQL. I'm trying to create a PROCEDURE to query annual salary for an specific employee. What is wrong with my code? Thanks.
CREATE OR REPLACE PROCEDURE annual_salary (
p_lname IN employees23.last_name%TYPE,
p_empid IN employees23.employee_id%TYPE)
IS
BEGIN
DECLARE
v_annualsal employees23.salary%TYPE;
BEGIN
SELECT salary*12
INTO v_annualsal
FROM employees23
WHERE last_name = p_lname;
DBMS_OUTPUT.PUT_LINE(v_annualsal);
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('No existe empleado');
END;
END annual_salary;
/
BEGIN
annual_salary('Russell');
END;
create table employees23 as
select 10 emp_id, cast('Russell' as varchar2(30)) last_name, 6000.00 salary
from dual;
create or replace procedure annual_salary (lname employees23.last_name%type) is
annualsal employees23.salary%type;
begin
select salary*12 into annualsal
from employees23
where last_name = lname
;
dbms_output.put_line (lname || ' has ' || annualsal || ' p.a.');
exception when no_data_found then
raise_application_error (-20000, 'emploee ' || lname || ' does not exists');
end annual_salary;
/
exec annual_salary('Russell');