SQL trigger to show salary change - sql

I am trying to create a simple trigger in sql developer to display the change in salary when it is changed
CREATE OR REPLACE TRIGGER salary_changes
BEFORE DELETE OR INSERT OR UPDATE ON FACULTY
FOR EACH ROW
DECLARE
sal_diff NUMBER;
BEGIN
sal_diff := :NEW.F_SALARY - :OLD.F_SALARY;
DBMS_OUTPUT.PUT_LINE('Difference: ' || sal_diff);
END;
when I attempt to run the trigger it prompts be to enter binds for NEW and OLD and when i try run an update to see if it works it states the trigger failed. So how am i using the old and new tags incorrectly? or is that not the issue

There are several issues with your code.
You need to create a after trigger instead of the before trigger.
You are trying to write a trigger that performs an operation for
insert,delete or update. So you should do the conditional checks
(such as if inserting,deleting or updating) clause.
Also, when you do delete, there is no new value, but just the old
value.
I would change your trigger as below..
CREATE OR REPLACE TRIGGER salary_changes
AFTER DELETE OR INSERT OR UPDATE ON FACULTY
FOR EACH ROW
DECLARE
sal_diff NUMBER;
BEGIN
If (INSERTING or UPDATING) then
sal_diff := :NEW.F_SALARY - :OLD.F_SALARY;
DBMS_OUTPUT.PUT_LINE('Difference: ' || sal_diff);
END IF;
IF DELETING THEN
DBMS_OUTPUT.PUT_LINE('The deleted value is:' || :OLD.F_SALARY);
END IF;
END;

Related

Multiple WHEN Statements in Oracle SQL

Can I get some help on below SQL trigger. Something is wrong with using multiple "WHEN" statements.
create or replace TRIGGER TRANS_TASKS_TRIG02
BEFORE INSERT OR UPDATE ON "TASKS"
REFERENCING FOR EACH ROW
WHEN(NEW.STATUS='WIP') BEGIN
IF INSERTING OR UPDATING THEN
:NEW.UPDATED_DATE := NEW_TIME(SYSDATE, 'GMT', 'PDT' );
END IF;
WHEN(NEW.STATUS<>'WIP') BEGIN
IF INSERTING OR UPDATING THEN
:NEW.UPDATED_DATE := NULL;
END IF;
END;
According to Oracle's documentation, you can't have multiple when clauses in a trigger.
You could create to separate triggers:
create or replace TRIGGER TRANS_TASKS_TRIG02_WIP
BEFORE INSERT OR UPDATE ON "TASKS"
REFERENCING FOR EACH ROW
WHEN(NEW.STATUS='WIP') BEGIN
IF INSERTING OR UPDATING THEN
:NEW.UPDATED_DATE := NEW_TIME(SYSDATE, 'GMT', 'PDT' );
END IF;
END;
create or replace TRIGGER TRANS_TASKS_TRIG02_WIP
BEFORE INSERT OR UPDATE ON "TASKS"
REFERENCING FOR EACH ROW
WHEN(NEW.STATUS<>'WIP') BEGIN
IF INSERTING OR UPDATING THEN
:NEW.UPDATED_DATE := NULL;
END IF;
END;
Or have a single trigger with an if statement in it. Note that the if inserting or updating condition is redundant, since the trigger is invoked only before insert or update:
create or replace TRIGGER TRANS_TASKS_TRIG02
BEFORE INSERT OR UPDATE ON "TASKS"
REFERENCING FOR EACH ROW
BEGIN
IF :NEW.STATUS='WIP' THEN
:NEW.UPDATED_DATE := NEW_TIME(SYSDATE, 'GMT', 'PDT' );
ELSIF :NEW.STATUS<>'WIP' THEN
:NEW.UPDATED_DATE := NULL;
END IF;
END;
/

Which schema has done insert, update or delete

actually I'm creating a before insert or update or delete trigger which will look on a table IMP_CUSTOMER and log the dataset which was updated, inserted or deleted into a table LOG_IMP_CUSTOMER. Everything work's fine but I just missing one point:
The data in the table can be changed by different database user and I'm trying to get the user which has done the change on the table to log that also into my log table.
This is my trigger until now:
CREATE OR REPLACE TRIGGER TRG_LOG_IMP_CUSTOMER
BEFORE INSERT OR UPDATE OR DELETE ON IMP_CUSTOMER
REFERENCING OLD AS old_buffer NEW AS new_buffer FOR EACH ROW
DECLARE
log_date TIMESTAMP;
sql_type VARCHAR(1);
log_user VARCHAR(10);
BEGIN
-- set log_date
log_date := SYSDATE;
-- set sql_type
IF INSERTING THEN sql_type := 'I';
END IF;
IF UPDATING THEN sql_type := 'U';
END IF;
IF DELETING THEN sql_type := 'D';
END IF;
-- set log_user
log_user := 'USER'; -- hardcoded for test
-- log update and delete
IF UPDATING OR DELETING THEN
INSERT INTO LOG_IMP_CUSTOMER VALUES (:old_buffer.CIF_ID,:old_buffer.PHONE_NUMBER,:old_buffer.PHONE_AREACODE,SEQ_LOG_IMP_CUSTOMER.nextval,log_date,sql_type,log_user);
END IF;
-- log insert
IF INSERTING THEN
INSERT INTO LOG_IMP_CUSTOMER VALUES
(:new_buffer.CIF_ID,:new_buffer.PHONE_NUMBER,:new_buffer.PHONE_AREACODE,SEQ_LOG_IMP_CUSTOMER.nextval,log_date,sql_type,log_user);
END IF;
END;
/
I just searching for any way to set log_user to the user which has done the change.
Some good ideas?
Thanks and regards,
David
you can edit like below.
log_user := sys_context('USERENV','SESSION_USER');

Trigger — is invalid and failed re-validation

I am trying to create this simple trigger :
CREATE OR REPLACE TRIGGER my_trig
BEFORE DELETE OR INSERT OR UPDATE ON empcopy
FOR EACH ROW
WHEN (NEW.EID > 0)
DECLARE
sal_diff number;
BEGIN
sal_diff := :NEW.salary - :OLD.salary;
dbms_output.put_line('Old salary: ' || :OLD.salary);
dbms_output.put_line('New salary: ' || :NEW.salary);
dbms_output.put_line('Salary difference: ' || sal_diff);
COMMIT;
END;
/
on execution it gives the result as:
Trigger created
But when I update my table the following result is obtained:
update empcopy
set salary=salary+5000;
after execution:
error at line 1
ORA-0498:triiger 'HR.MY_TRIG' is invalid and failed re-validation.
You can't COMMIT without using a PRAGMA AUTONOMOUS_TRANSACTION in your DECLARE block.
If the database allowed committing in row-level triggers, this could enable committing part of the rows in a statement before the others had been evaluated and would disrupt the statement as a unit--all rows in an individual statement should complete their change and be committed together.
If you were to use an AUTONOMOUS_TRANSACTION in this trigger, this would allow the trigger to execute new UPDATEs, DELETEs, etc in a transaction independent from other active DML changes in the open transaction.
But note, in you're case, your TRIGGER isn't actually executing any mutational DML, nor even any reads, so you don't need a COMMIT at all. All you need to do is drop your COMMIT as below.
CREATE OR REPLACE TRIGGER my_trig
BEFORE DELETE OR INSERT OR UPDATE ON empcopy
FOR EACH ROW
WHEN (NEW.EID > 0)
DECLARE
sal_diff number;
BEGIN
sal_diff := :NEW.salary - :OLD.salary;
dbms_output.put_line('Old salary: ' || :OLD.salary);
dbms_output.put_line('New salary: ' || :NEW.salary);
dbms_output.put_line('Salary difference: ' || sal_diff);
END;
/
I would however suggest a couple other changes as well.
As other triggers can also change the NEW value, you might consider making this an AFTER TRIGGER, to only log the final state.
Also this trigger will not log any DELETEs, since DELETEs will all have a NULL :NEW.EID. I'd suggest either dropping the AFTER DELETE if logging DELETEs is not intended, or handling DELETEs separately via a CASE WHEN DELETING statement.
CREATE OR REPLACE TRIGGER MY_TRIG
AFTER DELETE OR INSERT OR UPDATE ON EMPCOPY
FOR EACH ROW
DECLARE
SAL_DIFF NUMBER;
BEGIN
CASE WHEN DELETING
THEN
DBMS_OUTPUT.put_line('Log the delete here if you want.');
WHEN (:NEW.EID > 0)
THEN
SAL_DIFF := COALESCE(:NEW.SALARY, 0) - COALESCE(:OLD.SALARY, 0);
DBMS_OUTPUT.put_line('Old salary: ' || :OLD.SALARY);
DBMS_OUTPUT.put_line('New salary: ' || :NEW.SALARY);
DBMS_OUTPUT.put_line('Salary difference: ' || SAL_DIFF);
ELSE NULL;
END CASE;
END;
/
Also DBMS_OUTPUT is transient logging. In case you want to keep a record of changes to EMPCOPY more permanently, Oracle has tools available to automate and control change-tracking in your data, such as audit trail and FGA.
EDIT: examples below.
Create a test table:
CREATE TABLE EMPCOPY(
EID NUMBER NOT NULL,
SALARY NUMBER
);
Table EMPCOPY created.
Then create the trigger:
CREATE OR REPLACE TRIGGER MY_TRIG
AFTER DELETE OR INSERT OR UPDATE ON EMPCOPY
FOR EACH ROW
DECLARE
SAL_DIFF NUMBER;
BEGIN
CASE WHEN DELETING
THEN
DBMS_OUTPUT.put_line('Log the delete here if you want.');
WHEN (:NEW.EID > 0)
THEN
SAL_DIFF := COALESCE(:NEW.SALARY, 0) - COALESCE(:OLD.SALARY, 0);
DBMS_OUTPUT.put_line('Old salary: ' || :OLD.SALARY);
DBMS_OUTPUT.put_line('New salary: ' || :NEW.SALARY);
DBMS_OUTPUT.put_line('Salary difference: ' || SAL_DIFF);
ELSE NULL;
END CASE;
END;
/
Trigger MY_TRIG compiled
Then test it:
SQL> --Should not log, EMPID is not greater than zero.
SQL> INSERT INTO EMPCOPY VALUES (-13, 50000);
1 row inserted.
SQL> --Should log, EMPID is greater than zero.
SQL> INSERT INTO EMPCOPY VALUES (1919, 75000);
Old salary:
New salary: 75000
Salary difference: 75000
1 row inserted.
SQL> -- The statement you provided. This should log for EMPID=1919 but not EMPID=-13
SQL> update empcopy
2 set salary=salary+5000;
Old salary: 75000
New salary: 80000
Salary difference: 5000
2 rows updated.
SQL> -- This should log a PLACEHOLDER value for each row on delete.
SQL> DELETE FROM EMPCOPY;
Log the delete here if you want.
Log the delete here if you want.
2 rows deleted.

Trigger to prevent Any Deleting from Table

a trigger for this new rental history table that prevents deletions from the table.
CREATE OR REPLACE TRIGGER RENTALHIS_DEL
BEFORE DELETE ON RENTALHISTORY
BEGIN
dbms_output.put_line( 'Records can not be deleted');
END;
DELETE FROM RENTALHISTORY WHERE RENTALID = 1;
-- It is deleting before it says it can not delete
1 rows deleted.
Records can not be deleted
dbms_output.put_line( 'Records can not be deleted');
The above just prints the text and trigger completes successfully and then delete happens anyway. What you wanna do instead is to raise an error to prevent the program from completing.
Use standard procedure raise_application_error to stop the program and raise error:
CREATE OR REPLACE TRIGGER RENTALHIS_DEL
BEFORE DELETE ON RENTALHISTORY
BEGIN
raise_application_error(-20001,'Records can not be deleted');
END;
/
You want to raise an error, not print a message. The delete happens anyway:
CREATE OR REPLACE TRIGGER RENTALHIS_DEL
BEFORE DELETE ON RENTALHISTORY
BEGIN
RAISE_APPLICATION_ERROR (-20000, 'Deletion not supported on this table');
END;
Alternatively, you could use an instead of trigger to prevent the delete from taking place:
CREATE OR REPLACE TRIGGER RENTALHIS_DEL
INSTEAD OF DELETE ON RENTALHISTORY
BEGIN
dbms_output.put_line( 'Records cannot be deleted');
END;
You can make use of commit after your delete statement and rollback after that to reach to the previous stage using Pragma.
CREATE OR REPLACE TRIGGER
RENTALHIS_DEL
AFTER DELETE ON RENTALHISTORY
DECLARE
PRAGMA
AUTONOMOUS_TRANSACTION;
BEGIN
RAISE_APPLICATION_ERROR
(-20000, 'Deletion getting rolled
back');
ROLLBACK;
END;

Oracle SQL trigger results in compilation error

I have a problem with trigger not working. Here is my trigger code
create or replace trigger "ZIVOTINJE_T2"
BEFORE
insert or update on "ZIVOTINJE"
for each row
begin
IF new.cijena>10 THEN
:new.cijena:=9.9
ELSEIF new.cijena<0 THEN
:new.cijena:=0.1
END IF;
end;
When I try to insert entity in table ZIVOTINJE, I get this
ORA-04098: trigger 'DENISS.ZIVOTINJE_T2' is invalid and failed re-validation
I can see three problems in the code of your trigger:
You need to refer to the new values of the row using :new (including the colon), not new.
To change values that are about to be inserted/updated, write :new.cijena := 9.9; instead of SET new.cijena=9.9. Note that (a) there is no SET keyword here; (b) the assignment operator is :=, not =; and (c) you need a semi-colon at the end of the line.
Use ELSIF instead of ELSEIF.
Finally, in SQL*Plus, you can use SHOW ERRORS TRIGGER "ZIVOTINJE_T2" to show the errors for this trigger.
try this.
CREATE OR REPLACE TRIGGER "ZIVOTINJE_T2"
BEFORE
insert or update on "ZIVOTINJE"
for each row
begin
IF :new.cijena = 10 THEN
:new.cijena := 9.9;
ELSE
IF :new.cijena < 0 THEN
:new.cijena := 0.1;
END IF;
end if;
end;