Validating whether an emp is a manager in oracle - sql

I am trying to build a trigger that would not allow users to give a salary higher than 10,000 to employees but if the employee is a manager, I don't want the user to be able to give less than 15000 of a salary.
What I reached so far is the following
create or replace trigger sal_check
before update or insert on emp
for each row
begin
if :new.sal > 10000
then
update emp set sal = 10000;
end if;
end;
Now can I please take a hint of how to do a check whether the employee is a manager or not?
Note : I am using Oracle, and this is running on Scott schema, the one that actually comes with Oracle by default.

If emp table has mgr column and you set up foreign key constraint, it's enough just to check whether mgr field is not null. Also, update emp set sal = 10000; in your code will cause ORA-04091 (table is mutating), you need to change it to :new.sal := 1000. So you trigger will look like
...
if :new.mgr IS NULL THEN
if :new.sal > 10000
then
:new.sal := 10000;
end if;
ELSE
if :new.sal < 15000
then
:new.sal := 15000;
end if;
END IF;

I think you should create a table (not temporary) with all the managers.
Afterwords, it would be quite easy to detect if a employee is a manager or not and by making a simple select on that table you could see what should be the salary.
To determine what employees should be on the table, it would be the ones in the mgr column.

Related

there is few mistake in trigger help me to solve that problem

Create trigger that will
not allow update of salary for employee
who are in sales department.
Emp(no,name,dno,salary,designamtion)
Dept(dno,name,location)
Errors: TRIGGER MYTRIGGER1
Line/Col: 6/11 PLS-00049: bad bind variable 'OLD.EMP'
Line/Col: 6/31 PLS-00049: bad bind variable 'OLD.EMP'
DROP TABLE EMP;
DROP TABLE DEPT;
CREATE TABLE Dept
(
dno NUMBER PRIMARY KEY,
name VARCHAR(15),
location VARCHAR(15) );
CREATE TABLE Emp
(
no NUMBER PRIMARY KEY,
name VARCHAR(15),
dno NUMBER,
salary NUMBER,
designamtion VARCHAR(15),
FOREIGN KEY(dno) REFERENCES Dept(dno) );
insert into DEPT values (1,'SALES','GUJARAT');
insert into DEPT values (2,'MARKETING','UP');
insert into DEPT values (3,'MANUFACTURING','MP');
insert into DEPT values (4,'DEALING','VAPI');
insert into DEPT values (5,'SELL','TAPI');
insert into EMP values (1,'AMAN',2,45400,'MANAGER');
insert into EMP values (2,'BHAMAN',5,20000,'GM');
insert into EMP values (3,'CHAMAN',3,34400,'ADVISOR');
insert into EMP values (4,'DAMAN',4,75400,'WORKER');
insert into EMP values (5,'KHAMAN',1,42400,'MANAGER');
CREATE OR REPLACE trigger MYTRIGGER1
BEFORE UPDATE OF SALARY ON EMP
for each row
declare
hmmm VARCHAR(15);
begin
select Dept.name into hmmm
from Dept, Emp
where :old.emp.no=no and :old.emp.dno=Dept.dno;
dbms_output.put_line(hmmm);
end;
You don't need to alias the table the trigger is on. This will compile fine:
CREATE OR REPLACE trigger MYTRIGGER1
BEFORE UPDATE OF SALARY ON EMP
for each row
declare
hmmm VARCHAR(15);
begin
select Dept.name into hmmm
from Dept, Emp
where :old.no=no and :old.dno=Dept.dno;
dbms_output.put_line(hmmm);
end;
However, this trigger will fail when you update the table. You cannot select from the table itself in the trigger body. This will raise a mutating table error. Since it is an assignment I'll leave the research up to you.
As Koen said, your code will suffer from mutating table error.
Lucky you - you don't have to select from the table you're just updating (the emp table, right?) - you have everything you need in :new.dno. So:
SQL> create or replace trigger mytrigger1
2 before update of salary on emp
3 for each row
4 declare
5 hmmm varchar2(15);
6 begin
7 select d.name
8 into hmmm
9 from dept d
10 where d.dno = :new.dno;
11
12 dbms_output.put_line('Department name = ' || hmmm);
13 end;
14 /
Trigger created.
Testing:
SQL> set serveroutput on
SQL>
SQL> update emp set salary = 1000 where no = 1;
Department name = MARKETING
1 row updated.
SQL> update emp set salary = 1000;
Department name = MARKETING
Department name = SELL
Department name = MANUFACTURING
Department name = DEALING
Department name = SALES
5 rows updated.
SQL>
Your request is a trigger that disallows certain action for employees in Sales. Your trigger would not do so even it compiled. Two problems:
dbms_output does not prevent anything it merely 'prints' a message.
it prevents any update to salary, except when emp.dno or emp.no is also updated.
First (getting up on soapbox). You are enforcing a business rule. This should not be done in a trigger. It should be done in either the business rules engine of your application or in a database constraint or if you want to be super cautions both. Now a trigger will do the enforcement but business rule violations should glaringly obvious and easy to find, in a trigger becomes a side effect of an action, hidden away and difficult to find. (OK enough of that getting off soapbox).
The prevention method your looking for is raise_application_error. And you want a little code as possible in a trigger. In this case you have everything needed from the EMP table through the pesudo rows :old and :new so there is no reason to join your tables. However you do need to look at the specific department. It is too bad Oracle constrains the Exists predicate to introduction of a sub-select, this would be a perfect place for the structure:
If EXISTS (selects ...) then ...
But that is not available. The following uses a technique of reversing the requirement. That is it selects what is NOT wanted then if found it raise the exception, and if not found it suppress the Oracle raised error. So:
create or replace trigger mytrigger1
before update of salary on emp
for each row
declare
hmmm varchar2(01);
begin
select null
into hmmm
from dept d
where d.dno = :old.dno
and d.name = 'SALES';
raise_application_error(-20199, 'Can not update salary for Employee in Sales.');
exception
when no_data_found then null;
end mytrigger1;
This is sometimes referred to as "Programming the Exception" and is not a generally recommended procedure but at times it is useful. See fiddle here.
There is an outstanding question not addressed here. Can an employee in Sales have a salary change if they are also transferring departments. I.e. should the following succeed or fail?
update emp e
set dno = 2
, salary = salary + 5000
where e.no = 5;

Using trigger to update a value

How do I write a simple trigger to check when salary is updated it is not over 5% for an existing faculty member.
I have a block of code here.
create or replace TRIGGER TRG_Not_over_5per
BEFORE UPDATE OF F_SALARY ON FACULTY
DECLARE
sal FACULTY.F_SALARY%TYPE;
BEGIN
SELECT FACULTY.F_SALARY INTO sal FROM FACULTY
-- how do I use the WHERE CLAUSE here so that I get the salary affected
-- do some math on the first and second salary
-- if less than 5%, keep update, if not, reject update.
END;
Thanks in advance.
You don't need to use a SELECT in the trigger (*). Define the trigger as FOR EACH ROW and you can have access to the old and new values of any column belonging to the table.
create or replace TRIGGER TRG_Not_over_5per
BEFORE UPDATE OF F_SALARY ON FACULTY
FOR EACH ROW
So your code would look like:
if :new.f_salary < :old.f_salary * 1.05 then
raise_application_error (
-20000
, 'salary increase must be at least 5%'
);
end if;
This way of handling the rule violation is just a suggestion. You do whatever you need to. You don't need to handle the ELSE branch: Oracle will apply the update by default.
(*) In fact Oracle will hurl a mutating table exception if you do try to execute the query you want to write. Find out more.

How to fetch the deleted records?

How to fetch the deleted records. I am using Oracle 10g.
Do you have a backup before the deletion? ...or you could try using flashback (SELECT AS OF)
http://docs.oracle.com/cd/E11882_01/appdev.112/e41502/adfns_flashback.htm#ADFNS01003
Hope I've understood your question correctly.
Do some research into the RETURNING clause: https://docs.oracle.com/cd/B19306_01/appdev.102/b14261/returninginto_clause.htm
https://docs.oracle.com/cd/B19306_01/appdev.102/b14261/collections.htm#BABHDGIG
The link has a fully-functional example with an UPDATE statement. I'm sure if you spent some time on it you can figure out how to make it work with a DELETE statement as well.
DECLARE
TYPE EmpRec IS RECORD (last_name employees.last_name%TYPE,
salary employees.salary%TYPE);
emp_info EmpRec;
emp_id NUMBER := 100;
BEGIN
UPDATE employees SET salary = salary * 1.1 WHERE employee_id = emp_id
RETURNING last_name, salary INTO emp_info;
DBMS_OUTPUT.PUT_LINE('Just gave a raise to ' || emp_info.last_name ||
', who now makes ' || emp_info.salary);
ROLLBACK;
END;
/

TRIGGERS using HR database

I want to create a trigger “Salary_Not_Decrease” to ensure that the salary of an employee is not decreased during update of table Employee.
Please advise on how to start with creating such trigger.
I wrote the following code but not sure about it
CREATE OR REPLACE TRIGGER Salary_Not_Decrease
BEFORE INSERT OR UPDATE
ON employees
FOR EACH ROW
BEGIN
if :new.salary < :old.salary then
RAISE_APPLICATION_ERROR(-20001,'Salary should not be decreased ') ;
end if;
END;
Please advise
Your question indicates you want to prevent reductions in salary. You can't prevent a reduction in salary when there is no salary to reduce, so having this as a before insert trigger doesn't make sense. Make it just a before update trigger and you should be good to go also as pointed out in the comments you should check for nulls:
CREATE OR REPLACE TRIGGER Salary_Not_Decrease
BEFORE UPDATE
ON employees
FOR EACH ROW
BEGIN
if coalesce(:new.salary,:old.salary-1,0) < nvl(:old.salary,0) then
RAISE_APPLICATION_ERROR(-20001,'Salary should not be decreased ') ;
end if;
END;
Still reading the Documentation on triggers is a good place to learn more.

pl/sql procedure with savepoint

I have a table employee with columns: id, name, department, salary.
I want to create a procedure which increase salary for all employees in a specific department and run procedure if salary is less than 500, otherwise return to savepoint.
I don't know how to write the exception
CREATE PROCEDURE `procedure1` (IN dep1 INT(11), IN sal1 INT(11))
BEGIN
SAVEPOINT point1;
UPDATE employee SET salary=salary+sal1
WHERE department=dep1;
EXCEPTION
WHEN salary>500 THEN
ROLLBACK TO point1;
END
maybe you need to review your point of view, because you can add another WHERE clause in order to update only those salaries who are less than or equal to 500, e.g.:
UPDATE employee SET salary=salary+sal1
WHERE department=dep1
AND salary <= 500;
With this I think you don't need a SAVEPOINT, however find here how to use it: SAVEPOINT
Hope this help!!.
CREATE OR REPLACE PROCEDURE procedure1 (dep1 IN NUMBER, sal1 IN NUMBER)
BEGIN
UPDATE employee SET salary=salary+sal1
WHERE department= dep1 and
salary <= 500;
COMMIT;
END;