Write PL/SQL Procedure - sql

Write PL/SQL Procedure for the given Specification.
Assume the Table design is as follows :
Table Name :
employee_details
Column Name Data Type Constraint
Emp_ID NUMBER(5) NOT NULL Primary Key
Emp_Name VARCHAR2(100) NOT NULL
Date_Joined DATE NOT NULL
Gender CHAR(1) NOT NULL
Salary NUMBER(20,2) NOT NULL
Increment CHAR(1) NULL Y / N
Procedure is for incrementing the salary with the given value if the Increment Flag for the Employee is set to Y and commit the changes done
Procedure Name : PRC_INCREMENT
Input Parameters : a_Emp_ID, a_Salary
Output Parameters : a_Exec_Status
return messages by procedure
if Given employee is not found 'EMPLOYEE IS NOT FOUND'
Employee is found and Increment flag is not Y 'INCREMENT NOT APPLICABLE FOR EMPLOYEE' – [GIVEN EMPLOYEE ID]
Successfully update 'SUCCESSFULLY UPDATED FOR EMPLOYEE' – [GIVEN EMPLOYEE ID]
Any other unhandled exception ERROR with the information about ORACLE ERROR
below code i have tried, plx help me with procedure
CREATE OR replace PROCEDURE Prc_increment(a_emp_id IN VARCHAR2,
a_salary IN VARCHAR2)
AS
CURSOR c1 IS
SELECT emp_id,
salary,
increment_stat
FROM emp
WHERE emp_id = a_emp_id;
num_salary emp.salary%TYPE;
num_emp_id emp.emp_id%TYPE;
num_increment_stat emp.increment_stat%TYPE;
BEGIN
OPEN c1;
LOOP
FETCH c1 INTO num_emp_id, num_salary, num_increment_stat;
IF num_emp_id <> a_emp_id THEN
dbms_output.Put_line('Employee not found');
ELSIF num_increment_stat <> 'Y' THEN
dbms_output.Put_line('Employee not eligible for increment');
ELSE
UPDATE emp
SET salary = num_salary + a_salary
WHERE emp_id = a_emp_id
AND increment_stat = 'Y';
END IF;
EXIT WHEN c1% NOTFOUND;
END LOOP;
EXCEPTION
WHEN OTHERS THEN
dbms_output.Put_line(SQLERRM);
CLOSE c1;
END;

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.

PL/SQL : How to display subject title and lecturer names in a procedure?

My procedure will take in an subject code as a parameter and the proceed to display the subject title and lecturers’ names who have taught the subject before 2016. My procedure should also prompt input a subject code and display the results. I tried my code as following without putting it into a procedure and it returns the correct results but when I include my code as part of a PL/SQL procedure, I get an error that says
Warning: Procedure created with compilation errors.
I am quite new to PL/SQL so any advices would be helpful! The procedure code that I had been trying for hours as follow:
CREATE OR REPLACE PROCEDURE WHO(CODE CHAR)
IS
FNAME VARCHAR(256)
LNAME VARCHAR(256)
TITLE VARCHAR(256)
CURSOR C1
IS
SELECT FIRST_NAME, LAST_NAME, SUBJECT.NAME
FROM ACADEMIC INNER JOIN TEACHES
ON STAFF# = LECTURER
INNER JOIN SUBJECT
ON TEACHES.CODE=SUBJECT.CODE
WHERE YEAR<2016 AND TEACHES.CODE=CODE
GROUP BY FIRST_NAME, LAST_NAME, SUBJECT.NAME;
BEGIN
OPEN C1;
LOOP
IF C1%NOTFOUND THEN EXIT;
END IF;
FNAME=FIRST_NAME;
LNAME=LAST_NAME;
DBMS.OUTPUT.PUT_LINE(FNAME||' '||LNAME);
TITLE=SUBJECT.NAME;
DBMS.OUTPUT.PUT_LINE(TITLE);
END LOOP;
CLOSE C1;
END WHO;
/
You have few issues with your code. Some missing semilcons at decalration. See below:
CREATE OR REPLACE PROCEDURE WHO( CODE CHAR)
IS
FNAME VARCHAR(256); --<Missing Semicolon
LNAME VARCHAR(256); --<Missing Semicolon
TITLE VARCHAR(256); --<Missing Semicolon
CURSOR C1
IS
SELECT FIRST_NAME,
LAST_NAME,
SUBJECT.NAME
FROM ACADEMIC
INNER JOIN TEACHES
ON STAFF# = LECTURER
INNER JOIN SUBJECT
ON TEACHES.CODE =SUBJECT.CODE
WHERE YEAR <2016
AND TEACHES.CODE=CODE
GROUP BY FIRST_NAME,
LAST_NAME,
SUBJECT.NAME;
BEGIN
OPEN C1;
LOOP
--this is how you put the value of cursor to variable
FETCH C1 into FNAME ,LNAME ,TITLE ;
--No need for IF to exit loop. You can use as shown below
EXIT when C1%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(FNAME||' '||LNAME);
DBMS_OUTPUT.PUT_LINT(TITLE);
END LOOP;
CLOSE C1;
END WHO;
/
Demo:
CREATE OR REPLACE PROCEDURE WHO( CODE CHAR)
IS
FNAME number;
CURSOR C1
IS
SELECT contract_id FROM TX;
BEGIN
OPEN C1;
LOOP
fetch c1 into FNAME;
EXIT when C1%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(FNAME);
END LOOP;
CLOSE C1;
END WHO;
/
Output:
1
1
1
2
2
2
3
3
Tools like SQL Developer, PL/SQL Developer etc will highlight compilation errors, or on the SQL*Plus command line you can enter
show errors
immediately after compiling, or else
show errors procedure who
Or you can query the user_errors table. Whatever tool you are using, you need to be able to work with compilation errors.
Start by tidying up the code so it is easier to read and follow and generally looks more professional. Also although they are not actually causing any errors, you should really change those char and varchar types to the standard varchar2 (ideally everything should be anchored as academic.first_name%type etc, but one thing at a time).
Here is the fixed version:
create or replace procedure who
( code subject.code%type ) -- CHAR is a disastrous datatype you should never need
is
fname varchar2(50); -- VARCHAR2 is the standard string type, VARCHAR is reserved
lname varchar2(50); -- Also PL/SQL requires terminating semicolons here
title varchar2(256);
cursor c1 is
select first_name
, last_name
, subject.name
from academic
join teaches
on staff# = lecturer
join subject
on teaches.code = subject.code
where year < 2016
and teaches.code = who.code -- "code" on its own is ambiguous
group by first_name, last_name, subject.name;
begin
open c1;
loop
fetch c1 into fname, lname, title; -- Added 'fetch into'
if c1%notfound then
exit;
end if;
dbms_output.put_line(fname || ' ' || lname); -- It's dbms_output, with a '_'
dbms_output.put_line(title);
end loop;
close c1;
end who;
This compiles.
But you can still simplify this by using a Cursor FOR Loop instead of the elaborate, verbose way:
create or replace procedure who
( code subject.code%type )
is
begin
for r in (
select first_name
, last_name
, subject.name as title
from academic
join teaches
on staff# = lecturer
join subject
on teaches.code = subject.code
where year < 2016
and teaches.code = who.code
group by first_name, last_name, subject.name
)
loop
dbms_output.put_line(r.first_name || ' ' || r.last_name);
dbms_output.put_line(r.title);
end loop;
end who;
This implicitly declares a cursor and a record named r with the three fields based on the cursor, and it handles opening, fetching and closing for you.
I don't know your tables, but I am guessing they are something like this:
create table subject
( code varchar2(20) primary key
, name varchar2(30) not null );
create table academic
( staff# integer primary key
, first_name varchar2(20) not null
, last_name varchar2(20) not null );
create table teaches
( lecturer references academic(staff#) not null
, code references subject not null
, year number(4,0) not null
, constraint teaches_pk primary key (lecturer, code, year) );
If so, I would use table aliases in the query as below to avoid ambiguity:
select a.first_name
, a.last_name
, s.name as title
from academic a
join teaches t
on t.lecturer = a.staff#
join subject s
on s.code = t.code
where t.year < 2016
and t.code = who.code
group by a.first_name, a.last_name, s.name
Your going the correct direction, but there are a bunch of typos..
But to your main-question:
You have to FETCH the rows into you variables. Here an example.
DECLARE
FNAME VARCHAR (256); -- you need some semicolons here ;)
LNAME VARCHAR (256);
NAME VARCHAR (256);
TITLE VARCHAR (256);
CURSOR C1 -- replaced you query with a dummy-query. Yours should also work
IS
SELECT 'Hans' FIRST_NAME, 'Schnitzel' LAST_NAME, 'The Schnitzel' NAME
FROM DUAL
UNION ALL
SELECT 'Heinrich' FIRST_NAME, 'Wurst' LAST_NAME, 'The Sausage' NAME
FROM DUAL;
BEGIN
OPEN C1; -- open cursor
LOOP
FETCH C1 INTO FNAME, LNAME, NAME; -- try to read the row and put the values into our variables
IF C1%NOTFOUND -- check if we got a row
THEN
EXIT;
END IF;
DBMS_OUTPUT.put_line ('Name: ' || FNAME || ' ' || LNAME); -- work with is.
TITLE := NAME;
DBMS_OUTPUT.put_line ('Title: ' || TITLE);
END LOOP;
CLOSE C1;
END;

How to start procedure in oracle?

So I have this SELECT statement:
SELECT emp.emp_name, pos_emp.POSITION_NAME
from EMPLOYEE emp
join POSITION_EMPLOYEE pos_emp
on emp.POSITION_EMPLOYEE_POSITION_ID=pos_emp.POSITION_ID
where emp.EMP_NAME='&employee_name';
When I enter employee name from keyboard it returns me his name(from EMPLOYEE table) and position(from POSITION_EMPLOYEE table). But I want to do this with stored procedure:
create or replace PROCEDURE emp_pos( EMPLOYEE_NAME IN EMPLOYEE.EMP_NAME%TYPE,
POSITION_NAME OUT POSITION_EMPLOYEE.POSITION_NAME%TYPE )
AS
BEGIN
SELECT pos_emp.POSITION_NAME
INTO
POSITION_NAME
FROM EMPLOYEE emp
JOIN
POSITION_EMPLOYEE pos_emp
ON
emp.POSITION_EMPLOYEE_POSITION_ID
= pos_emp.POSITION_ID
WHERE emp.EMP_NAME
= EMPLOYEE_NAME
;
END;
I am trying to start the procedure with begin:
begin emp_pos('&employee_name');
end;
The compiler gives me error:wrong number or types of arguments in call to 'EMP_POS'. Where am I wrong?
SQL>set serveroutput on;
SQL> create or replace PROCEDURE emp_pos( EMPLOYEE_NAME IN EMPLOYEE.EMP_NAME%TYPE,
POSITION_NAME OUT POSITION_EMPLOYEE.POSITION_NAME%TYPE )
AS
BEGIN
SELECT pos_emp.POSITION_NAME
INTO
POSITION_NAME
FROM EMPLOYEE emp
JOIN
POSITION_EMPLOYEE pos_emp
ON
emp.POSITION_EMPLOYEE_POSITION_ID
= pos_emp.POSITION_ID
WHERE emp.EMP_NAME
= EMPLOYEE_NAME
;
dbms_output.put_line(EMPLOYEE_NAME||'''s position is : '||POSITION_NAME);
END;
after defining this procedure, you may use in this way :
SQL>var v_emp_pos varchar2;
SQL>exec emp_pos('&employee_name',:v_emp_pos);
SQL>print v_emp_pos; -- just to print out this "out" parameter.

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;

select statement not working in procedure

create table Employee(Id number,
Name varchar(20),
Age number,
DId number,
Salary number,
primary key(Id),
foreign key(DId) references Department on delete cascade);
declare
total number;
procedure myFunction(x in number) is
begin
insert into Employee values(17,'Jaskaran Singh',31,1,200000);
dbms_output.put_line('successfully executed');
select * from Employee;
end;
begin
myFunction(3);
end;
To return data from stored procedure, you should create a cursor and return that select like the following:
CREATE OR REPLACE PACKAGE TYPES
AS
TYPE DATA_CURSOR IS REF CURSOR;
END;
then in your code is going to be like:
CREATE OR REPLACE PROCEDURE MYPROC(RESULTSET OUT TYPES.DATA_CURSOR) AS
BEGIN
INSERT INTO EMPLOYEE VALUES(17,'Jaskaran Singh',31,1,200000);
DBMS_OUTPUT.PUT_LINE('successfully executed');
OPEN RESULTSET FOR
SELECT * FROM EMPLOYEE;
END;
The Execution part like
DECLARE THE_RESULT_SET OUT TYPES.DATA_CURSOR;
BEGIN
MYPROC(3, THE_RESULT_SET);
-- You can now get the THE_RESULT_SET and take the result from it...
END;
Important: if you want to print as I understand the other case, you can get that result (same code), and loop whatever you want and print the result from the THE_RESULT_SET
If you want to print what's in the EMPLOYEES table you have loop a cursor over the EMPLOYEES table, printing each row appropriately. Here's an example:
DECLARE
TOTAL NUMBER;
PROCEDURE MYFUNCTION(X IN NUMBER) IS
BEGIN
INSERT INTO EMPLOYEE VALUES(17,'Jaskaran Singh',31,1,200000);
DBMS_OUTPUT.PUT_LINE('successfully executed');
FOR aRow IN (SELECT * FROM EMPLOYEE) LOOP
DBMS_OUTPUT.PUT_LINE('ID=' || aRow.ID ||
' NAME=''' || aRow.NAME || '''' ||
' AGE=' || aRow.AGE ||
' DID=' || aRow.DID ||
' SALARY=' || aRow.SALARY);
END LOOP;
END;
BEGIN
MYFUNCTION(3);
END;
Share and enjoy.