I want to Write a PROCEDURE that will first print the Employee Number and Salary of an employee (i.e. 7839). Then it will increase the salary of an employee 7839 (this will be employee number in the table employee) as per following conditions:
Condition-1: If experience is more than 10 years, increase salary by 20%.
Condition-2: If experience is greater than 5 years, increase salary by 10%.
Condition-3: All others will get an increase of 5% in the salary.
The program will print the Employee Number and salary before and after the increase i tried the following steps but not sure how accurate is it..
I need to convert it to a PROCEDURE code.
please advise
DECLARE
veno emp.empno%type:=&veno;
vsal emp.sal%type;
vexp number;
BEGIN
select empno,sal,trunc(to_char(months_between(sysdate,hiredate)/12))into veno,vsal,vexp from emp where empno=veno;
DBMS_OUTPUT.PUT_LINE('before update:' ||chr(10)||veno||chr(10)||vsal);
if vexp>=10 then
update emp set sal=sal+(sal*.20) where empno=veno;
select sal into vsal from emp where empno=veno;
DBMS_OUTPUT.PUT_LINE('after update:' ||chr(10)||vsal);
elsif vexp>=5 then
update emp set sal=sal+(sal*.10) where empno=veno;
select sal into vsal from emp where empno=veno;
DBMS_OUTPUT.PUT_LINE('after update:' ||chr(10)||vsal);
else
update emp set sal=sal+(sal*.05) where empno=veno;
select sal into vsal from emp where empno=veno;
DBMS_OUTPUT.PUT_LINE('after update:' ||chr(10)||vsal);
end if;
END;
/
All you need to change is the DECLARE (indicating the start of an anonymous block) to CREATE PROCEDURE, with the variable you're currently setting via a substitution variable as a formal argument; so instead of:
DECLARE
veno emp.empno%type:=&veno;
vsal emp.sal%type;
vexp number;
BEGIN
...
END;
/
Make it:
CREATE OR REPLACE PROCEDURE my_proc (veno IN emp.empno%type)
AS
vsal emp.sal%type;
vexp number;
BEGIN
...
END;
/
You can then call that from an anonymous block, or in SQL*Plus or SQL Developer with the execute shorthand:
set serveroutput on
execute my_proc(&veno);
This example is still using a substitution variable so you'll be promoted for the value to use, but you can pass a number directly too.
Read more about creating procedures and the types of parameters.
You could simplify the code quite a bit to reduce repetition and requerying; look up case expressions and the returning clause. But that's not directly relevant.
Related
I have created Procedure which calculates gross salary of all employees from salary table.When executing stored procedure using execute statement Error:"invalid SQL statement" occurs and when i am executing procedure using PLSQL block then Error":PLS-00905 Object HR.PROC_GROSSSALARY is invalid" occurs
-- Creating Stored procedure --
create or replace procedure proc_grosssalary
AS
begin
select s.*,(Basic+HRA+DA+CA+Medical) Gross_Salary from salary s;
end;
-- Calling SP using EXECUTE --
execute proc_grosssalary;
-- Calling SP using PLSQL Block --
begin
proc_grosssalary;
end;
display all data in salary table with calculated Gross_Salary in the form of table structure
your PL/SQL syntax is not correct
create or replace procedure proc_grosssalary (out gross_salary number)
AS
begin
select (Basic+HRA+DA+CA+Medical)
into gross_salary
from salary s;
end;
You should also consider the option of creating a View.
Create or replace view v_grosssalary
as select s.*,(Basic+HRA+DA+CA+Medical) Gross_Salary
from salary s;
and simply do select * from v_grosssalary to get the output.
With procedures, there's also a PRINT command(which works in SQL* Plus and SQL developer) using CURSOR bind variables
VARIABLE x REFCURSOR
create or replace procedure proc_grosssalary
AS
begin
OPEN cursor :x for
select s.*,(Basic+HRA+DA+CA+Medical) Gross_Salary from salary s;
end;
/
Print x -- This will display the required result.
But.. What do you want to do? If you want calculate salary for every employee and you have your information in the same row, with a simple SQL you could have it, something like this:
Select id_emp, name_emp, surname_emp, (Basic+HRAs+DA+CA+Medical) as salary
from salary;
And it would be more quick than have strange functions and procedures.. If you don't like it, create a view like these and you'll have a column with the salary calculated, more clean and less obscure
You need to use a cursor so that you can loop on records in the table.
Here is what I did -
#create a table for sample data
create table tsalary0918(Basic number,HRA number,DA number,CA number,Medical number)
#inserting sample data
insert into tsalary0918 values (100,100,100,100,100)
#checking if data is inserted
select * from tsalary0918;
#query output
BASIC HRA DA CA MEDICAL
100 100 100 100 100
#create a stored procedure
create or replace procedure testproc
AS
cursor cur is select s.*,(Basic +HRA + DA +CA +Medical ) Gross_salary from tsalary0918 s;
begin
for records in cur
loop
dbms_output.put_line('DA is ' || records.DA);
dbms_output.put_line('Gross Salary is ' || records.Gross_salary);
end loop;
end;
/
#execute the stored procedure
begin
testproc;
end;
/
#output of the above execution
dbms_output:
DA is 100
Gross Salary is 500
Check online - https://dbfiddle.uk/?rdbms=oracle_11.2&fiddle=1744281398fe62a6cb42250641947249
Task completed:
create or replace procedure p1
IS
cursor c1 is select s.*,(Basic+HRA+CA+DA+Medical) Gross_Salary from salary s;
emp_rec c1%rowtype;
begin
open c1;
dbms_output.put_line('Emp_No Basic HRA Gross_Salary');
dbms_output.put_line('---------------------------------------');
loop
fetch c1 into emp_rec;
exit when c1%notfound;
dbms_output.put_line(emp_rec.employee_number||' '||emp_rec.Basic||' '||emp_rec.HRA||' '||' '||emp_rec.Gross_Salary);
end loop;
close c1;
end;
-- Executing the procedure using PLSQL block --
begin
p1;
end;
Below is expected Final result:
----------------------------------------------
Employee_number Basic HRA Gross_salary
----------------------------------------------
1 25000 10000 36750
2 7000 2800 11650
3 10000 4000 15950
declare
type rec_1 is record(
e_nm varchar2(10),
e_no number );
r1 rec_1;
--select EMPNO, ename into r1 from emp where empno = 7369;
procedure proc_t1(r1 rec1) as
begin
select EMPNO, ename
into r1.e_no, r1.e_nm
from emp
where empno = 7369;
dbms_output.put_line(r1.e_nm);
end;
I am getting syntax error after writing this code. Can any one pls tell me why this syntax error is coming?enter image description here
I can't tell what the code is meant to do, but perhaps you meant something like this:
declare
procedure proc_t1
is
type emp_rectype is record
( id emp.empno%type
, name emp.ename%type );
l_emp emp_rectype;
begin
select empno, ename into l_emp.id, l_emp.name
from emp
where empno = 7369;
dbms_output.put_line(l_emp.name);
end;
begin
proc_t1();
end;
Notes:
There is no point declaring a record type outside the procedure unless you intend to reuse it elsewhere, which you don't in this example. If you want to reuse it, move it above the procedure declaration.
Procedure parameters default to IN mode, which is read-only, so in your example your select into would fail. It doesn't make sense to overwrite a parameter that was passed in, unless you want to transform it and pass it back out as a result.
This is my Function
create or replace
function sal_incr
(
p_grade number)
return number
is
v_inc number;
begin
select raise_percent into v_inc from sal_inc where grade_id = p_grade;
return 1 + (v_inc/100);
end;
This is my procedure:
create or replace
procedure sal_increm
is
begin
UPDATE emp_task SET sal = sal * sal_incr(grade_id);
end;
how to do that package.. without using triggers how to update "old sal","modified by" and "modified on" in separate table
You can have multiple DML statements in a procedure; they'd be rather less useful if you couldn't. You can do a single insert into your history table based on the data in the task table, adding the executing user with the USER function and and the current time with SYSDATE.
create or replace
procedure sal_increm
is
begin
insert into emp_sal_history (empno, old_sal, modified_by, modified_on)
select empno, sal, user, sysdate
from emp_task;
update emp_task set sal = sal * sal_incr(grade_id);
end;
/
If you want to record the new salary as well you can calculate that in the insert too.
This will record a history record even for employees whose grade doesn't get an increment. If you have those or want to handle that possibility and exclude them, you can add
where sal_incr(grade_id) != 1
You could add that to the update as well.
I have a task:
Write a procedure to update salary (salary * % of increment) in emp table based on grade. Use function to get increment
This is my procedure:
CREATE OR REPLACE
PROCEDURE sal_incre
IS
CURSOR c_cur
IS
SELECT * FROM emp_task;
BEGIN
UPDATE emp_task SET sal = sal + sal_incr(grade_id);
FOR rec IN c_cur
LOOP
dbms_output.put_line(rec.empno||','||rec.ename||','||rec.sal);
END LOOP;
END;
This is my function code:
CREATE OR REPLACE
FUNCTION sal_incr(
p_grade NUMBER)
RETURN
IS
v_inc NUMBER;
BEGIN
SELECT raise_percent
INTO v_inc
FROM sal_inc
WHERE grade_id IN
(SELECT grade_id FROM emp_task WHERE grade_id = p_grade
);
RETURN v_inc;
COMMIT;
END;
When I call the procedure I'm getting:
ORA-04091: table SCOTT.EMP_TASK is mutating, trigger/function may not see it
ORA-06512: at "SCOTT.SAL_INCR", line 8
ORA-06512: at "SCOTT.SAL_INCRE", line 6
ORA-06512: at line 2
What am I doing wrong?
Your function is referring to the same table you're using in the procedure at the point you call that function, which is what causes this error. You're updating and querying it at the same time, in a way that could cause indeterminate (or confusing) results, even though you aren't querying the column you're updating. Oracle is protecting you from yourself here.
In your function you're doing:
SELECT raise_percent
INTO v_inc
FROM sal_inc
WHERE grade_id IN
(SELECT grade_id FROM emp_task WHERE grade_id = p_grade
);
There is no need to look at the emp_task table here. Unless you've been passed an nonexistent value (which can't happen from your procedure) the subquery can only return the original p_grade argument value, so this is the same as:
SELECT raise_percent
INTO v_inc
FROM sal_inc
WHERE grade_id = p_grade;
If you do that the function no longer refers to emp_task, so it won't throw the mutating trigger/function error when it's called as part of an update.
And your function should not be issuing a COMMIT - let the calling procedure, or preferably the session that calls the procedure, decide whether the who transaction should be committed or rolled back.
Also, from the title and column name it looks like raise_percent is a percentage, so you need to use that to find the value to multiply by - you shouldn't add that percentage figure. If that gives you a value of 2 for a 2% raise, for example, you need to either do this in your procedure:
UPDATE emp_task SET sal = sal * (1 + (sal_incr(grade_id)/100));
Or more neatly have your function return 1 + (raise_percent/100) and do:
UPDATE emp_task SET sal = sal * sal_incr(grade_id);
Change the procedure like this:
create or replace
procedure sal_incre
is
cursor c_cur is
select distinct e.grade_id,e.ename,e.sal from sal_inc s
join emp_task e on e.grade_id = s.grade_id order by e.ename ;
v_incr number;
begin
for f_cur in c_cur
loop
v_incr := sal_incr(f_cur.grade_id);
update emp_task set sal = sal + v_incr;
dbms_output.put_line('Emp Name : '||f_cur.ename||','
||' Sal:'||f_cur.sal||','||' Grade: '||f_cur.grade_id);
end loop;
end;
I have created a procedure. It is giving an error ( ORA-01422: exact fetch returns more than requested number of rows). As for a specific department_id there are more than one employees. But how to solve this problem ?
Create Procedure PP1
(ID in number, Percent in number, Sal out number, increase_sal out number) IS
Begin
Select salary, salary *(1+percent/100) into sal, increase_sal
From employees
where department_id= id;
DBMS_OUTPUT.PUT_LINE (sal || ' ' || increase_sal);
END;
/
Variable a number
Variable b number
Exec PP1 (100, 10, :a, :b)
Print a b
Thanks,
Kuntal Roy
My guess is that you want something like (untested)
CREATE TYPE num_tbl IS TABLE OF NUMBER;
CREATE PROCEDURE raise_dept_salaries( p_dept_id IN employees.department_id%type,
p_raise_pct IN NUMBER,
p_old_sals OUT num_tbl,
p_new_sals OUT num_tbl )
AS
BEGIN
SELECT salary
BULK COLLECT INTO p_old_sals
FROM employees
WHERE department_id = p_dept_id;
UPDATE employees
SET salary = salary * (1 + p_raise_pct)
WHERE department_id = p_dept_id
RETURNING salary
BULK COLLECT INTO p_new_sals;
END;
Now, splitting things up this way does introduce the possibility that some other session will modify the data between your first SELECT and your UPDATE so this isn't really safe to use in a multi-user environment. Of course, you really wouldn't want to return both the old and the new salaries in the first place since you already know that they are going to be directly related to each other. If you only returned the collection of new salaries, then you would only need a single UPDATE statement and you wouldn't have the race condition.