SQL trigger - nesting INSERT INTO in IF condition - oracle

I want to create trigger registring in new table employees whom salary is being raised above 5000 and didn't get salary higher than 5000 so far.
Trigger I wrote is returning error Error(2,41): PL/SQL: ORA-00984: column not allowed here.
Here's my trigger:
CREATE OR REPLACE TRIGGER emp_gotrich_trig BEFORE UPDATE OF salary ON employees
FOR EACH ROW BEGIN
IF :NEW.salary>5000 AND :OLD.salary<=5000 THEN
INSERT INTO emp_gotrich VALUES (employee_id, SYSDATE, :OLD.salary, :NEW.salary);
END IF;
END;
And here's emp_gotrich table:
CREATE TABLE emp_gotrich ( emp_id NUMBER(6), raise_date DATE, old_sal NUMBER(8,2), new_sal NUMBER(8,2) );
I guess that INSERT statemet isn't nested properly but i don't know what should i change.
I also tried to use 'WHEN' but i dont know where should i omitt colons, so it doeasn't work too.
CREATE OR REPLACE TRIGGER emp_getrich_log BEFORE UPDATE OF salary ON employees FOR EACH ROW
WHEN
NEW.salary>5000 AND OLD.salary<=5000;
BEGIN
INSERT INTO emp_gotrich VALUES(employee_id, SYSDATE, :OLD.salary, :NEW.salary);
END;
Please, help me find a way to run it.

You forgot to specify :OLD or :NEW on the employee_id value in your INSERT statement. I believe it should be:
INSERT INTO emp_gotrich
(EMP_ID, RAISE_DATE, OLD_SAL, NEW_SAL)
VALUES
(:OLD.employee_id, SYSDATE, :OLD.salary, :NEW.salary);
I suggest that a field list, such as the one I added, should always be included in an INSERT statement.

Related

How can we Create a Trigger(or any Object) to insert rows into Attendance table when a certain time of day passes?

I am new to PL/SQL and Oracle APEX. In oracle APEX, I'm trying to create a trigger that will fire when employees are not present after a certain time, say for example 12:00 PM and it's not friday.
I have these columns in the Attendances Table: emp_id, work_date, attend_stat.
The emp_id is a foreign key referenced from Employees Table
When it's past 12:00 PM, and there was no attendance entry for a particular employee, I want a row inserted for that employee like this:
insert into attendances(emp_id, work_date, attend_Stat)
values("ID OF NOT PRESENT EMPLOYEE", sysdate, 'A');
I have written this pl/sql statement but I cannot find a way to implement it or figure out will it even work.
declare
cursor c_emp_id is
select emp_id from employees;
cursor c_emp_at is
select emp_id from attendances where work_date = sysdate and to_char(sysdate,'DAY') <> 'FRIDAY';
begin
for i in c_emp_id loop
for a in c_emp_at loop
if i.emp_id <> a.emp_id then
insert into attendances(emp_id, work_date, attend_stat)
values(i.emp_id, systimestamp, 'A');
end if;
end loop;
end loop;
exception
when no_data_found then
for i in c_emp_id loop
insert into attendances(emp_id, work_date, attend_stat)
values(i.emp_id, systimestamp, 'A');
end loop;
end;
How can I achieve this?
You need to save the code in a package or in a stored procedure and set it to run as a database job. See the documentation for details.
Please note the doc is for version 11 of the database, check the version you are on
select version from v$instance
and google for actual docs

Creating a trigger to insert and remove rows

I have been given an assignment to create a trigger that works when tables are inserted or updated or deleted in a table. If it is a delete or update then the table must store the older values before the action on another table. If it is insert then the new row should be added to the new table. Also it should include the number of rows that are affected by each action. So far this is what I have done:
CREATE OR REPLACE TRIGGER archive_update
BEFORE INSERT OR UPDATE ON EMPLOYEE
FOR EACH ROW
BEGIN
INSERT INTO archive_emp(EMP_ID, FIRST_NAME, LAST_NAME, BIRTH_DAY,
SEX, SALARY, SUPER_ID, BRANCH_ID)
VALUES(:new.EMP_ID, :new.FIRST_NAME, :new.LAST_NAME,
:new.BIRTH_DAY, :new.SEX, :new.SALARY, :new.SUPER_ID, :new.BRANCH_ID);
END;
To check the dml operation you may use an IF condition with appropriate dml predicate
CREATE OR REPLACE TRIGGER archive_update
BEFORE
INSERT OR UPDATE ON EMPLOYEE
FOR EACH ROW
BEGIN
IF UPDATING OR DELETING THEN --You may add this
INSERT INTO archive_emp( emp_id, first_name, last_name,
birth_day, sex,salary,super_id,branch_id
) VALUES (:old.emp_id, :old.first_name,:old.last_name,:old.birth_day,
:old.sex,:old.salary,:old.super_id,:old.branch_id
);
ELSIF INSERTING THEN --and this
INSERT INTO archive_emp( emp_id, first_name, last_name,
birth_day, sex,salary,super_id,branch_id
) VALUES (:new.emp_id, :new.first_name,:new.last_name,:new.birth_day,
:new.sex,:new.salary,:new.super_id,:new.branch_id
);
END IF;
END;
/

What is the proper way to create an update trigger with a variable getting value from the same table?

I'm creating a update trigger where an employee can never have a salary that is greater than the president's. However I need to subquery the president's salary for comparison and the "new" updated employee's salary.
I originally had the the subquery using from the from employees table but had to make a new table because of the mutating table problem. I don't think creating a new table is plausible solution for a real implementation.
Is there a way I can save the president's salary without creating a new table?
CREATE OR REPLACE TRIGGER prevent_salary
BEFORE UPDATE ON employees
FOR EACH ROW
declare pres_sal number(8,2);
BEGIN
select salary into pres_sal from employees_salary where job_id='AD_PRES';--employees_salary was employees but that gives mutating error
IF (:new.salary > pres_sal)
THEN UPDATE employees
SET salary = :old.salary
WHERE employee_id = :old.employee_id;
END IF;
END;
One way to do it is to save off the president's salary in a BEFORE STATEMENT trigger and then use that in the FOR EACH ROW trigger.
"Compound Triggers", which have been around at least since version 11.1, offer a nice way to do that all in one place.
Here is an example:
CREATE OR REPLACE TRIGGER prevent_salary
FOR UPDATE OF salary ON employees
COMPOUND TRIGGER
pres_sal NUMBER;
BEFORE STATEMENT IS
BEGIN
select salary
into pres_sal
from employees
where job_id='AD_PRES';
END BEFORE STATEMENT;
BEFORE EACH ROW IS
BEGIN
:new.salary := least(:new.salary, pres_sal);
END BEFORE EACH ROW;
END prevent_salary;
You may try this :
CREATE OR REPLACE TRIGGER prevent_salary
BEFORE UPDATE ON employees
FOR EACH ROW
declare pres_sal number(8,2);
BEGIN
select salary into pres_sal from employees_salary where job_id='AD_PRES';--employees_salary was employees but that gives mutating error
IF (:new.salary > pres_sal)
Raise_Application_Error (-20101, 'An employee''s salary couldn''t exceed president''s !');
END IF;
END;

Setting a column on a new row with a trigger

I have a table name dblog where the schema is like
data_balance_id number(8) primary key,
plan_id number(6) not null,
start_date date default current_date,
end_date date not null);
So I am trying to create a trigger which will update enddate column when a insertion is going to happen. enddate will be updated as 30 days from the insertion day. My trigger code is
CREATE OR REPLACE TRIGGER trg
BEFORE INSERT
ON dblog FOR EACH ROW
BEGIN
INSERT INTO dblog (end_date) values (SYSDATE()+30);
END;
/
The insert query is like following
insert into dblog (db_id, planid) values (12,123);
Trigger is created without any error. But at the time of insertion I am getting the following error
insert into dblog (db_id, planid) values (12,123)
*
ERROR at line 1:
ORA-00036: maximum number of recursive SQL levels (50) exceeded
ORA-06512: at "E1038351.TRG1", line 2
You just want to modify the :new pseudo-record. Something like this
CREATE OR REPLACE TRIGGER trg
BEFORE INSERT ON dblog
FOR EACH ROW
BEGIN
:new.end_date := sysdate + 30;
END;
If you don't want end_date to have a time component (or, rather, you want the time component to be midnight), you would want to trunc(sysdate) + 30.
You are trying to insert another row, which also re-triggers the trigger.
In the triggers you have access to the row using variables :NEW and :OLD.
In case when you insert, the :OLD is null , because you doesn't have it in the table yet.
So before inserting row you update his columns like this:
:NEW.END_DATE = SYSDATE+30;

Oracle 'statement level' Trigger

I want to create a Statement level Trigger which means I want to insert only one record into table EMP_AUDIT when 1 or more rows are inserted into table EMP. For example: if I have 10 records inserted into EMP, then only 1 record should be inserted into EMP_AUDIT table.
There are no constraints on columns. (i.e. can be NULL)
I tried to use the following trigger but it gives me the Error(2,2): PL/SQL: SQL Statement ignored
Error(2,14): PL/SQL: ORA-00947: not enough values
CREATE OR REPLACE
TRIGGER TRIG_EMP AFTER INSERT ON EMP
BEGIN
INSERT INTO EMP_AUDIT
VALUES (TRANID,EMPNUM,SYSDATE);
END;
CREATE TABLE EMP
(TRANID NUMBER,
EMPNUM VARCHAR2(100),
EMPLOC VARCHAR2(100));
CREATE TABLE EMP_AUDIT
(EVENTID NUMBER,
EMPNUM VARCHAR2(100),
ENTRDATE DATE);
The statement-level trigger (which you have) cannot see the data that was inserted. After all, if there were 10 rows inserted, what values should the columns be for your audit table?
You need a row-level trigger for this to work, e.g.:
CREATE OR REPLACE
TRIGGER TRIG_EMP
AFTER INSERT ON EMP
FOR EACH ROW
BEGIN
INSERT INTO EMP_AUDIT
VALUES (:NEW.TRANID,:NEW.EMPNUM,:NEW.SYSDATE);
END;
Use this piece of code:
CREATE OR REPLACE TRIGGER
TRIG_EMP
AFTER INSERT ON EMP
FOR EACH ROW
BEGIN
INSERT INTO EMP_AUDIT
VALUES (:NEW.TRANID,:NEW.EMPNUM,:NEW.SYSDATE);
END;

Resources