I would like to create a trigger that store Who(Names) as the Loged User, Action as what he/she has done, And the Time he/she made it. But Also it should be capable of revolking the changes when it's not Working Time for example working time can be (from 8H:00 AM to 05:00 PM). So Tried to create it.
// This is the Table to Store the Log named "System_events"
create table system_events (who varchar2(10),action varchar2(10), when date);
Actually the trigger will check and save any change on a table named students
//Codes to create that trigger
create or replace trigger all_actions
before insert or update or delete on students
declare
user_action system_events.action%type;
begin
if INSERTING then
user_action :='Insert';
elsif UPDATING then
user_action :='Update';
elsif DELETING then
user_action :='Delete';
else
raise_application_error (-20001, 'Yous should never get this error.');
end if;
insert into system_events(who,action,when) values(user,user_action,sysdate);
end;
So this trigger I created is working. However it doesn't check the Time. So I would like to add that feature of checking the Time and revolke the change if it's not in the right time
Thank you!!
It is not clear what do you mean by "revolke the change". I assume you want to stop the users from doing any DML operation except during work hours. If so, you could add another IF condition and raise an exception for times beyond 8am - 5pm.
CREATE OR replace TRIGGER all_actions
BEFORE INSERT OR UPDATE OR DELETE ON students
DECLARE
user_action system_events.action%TYPE;
BEGIN
IF inserting THEN
user_action := 'Insert';
ELSIF updating THEN
user_action := 'Update';
ELSIF deleting THEN
user_action := 'Delete';
ELSE
raise_application_error (-20001, 'You should never get this error.');
END IF;
IF to_number(to_char(SYSDATE, 'HH24')) NOT BETWEEN 8 AND 16 THEN
raise_application_error (-20001, 'You should not do '
||user_action
||' operation during this time.');
END IF;
INSERT INTO system_events
(who,
action,
when)
VALUES (USER,
user_action,
SYSDATE);
END;
/
Note: You may put this IF condition before checking the operation type as well if you want, without user_action
Related
Hello fellow programmers and happy new year to you all!
I have few university tasks for winter break and one of them is to create trigger on table:
PERSON(ID, Name, Surname, Age);
Trigger is supposed to inform user when they have inserted row with invalid ID. Vadility criteria is that ID is 11 digits long.
I tried to write solution like this:
CREATE OR REPLACE TRIGGER person_id_trigg
AFTER INSERT
ON person
DECLARE
idNew VARCHAR(50);
lengthException EXCEPTION;
BEGIN
SELECT id INTO idNew FROM INSERTED;
IF LENGTH(idNew) <> 11 THEN
RAISE lengthException;
END IF;
EXCEPTION
WHEN lengthException THEN
dbms_output.put_line('ID for new person is INVALID. It must be 11 digits long!');
END;
Then I realized that INSERTED exists only in sqlserver and not in oracle.
What would you suggest I could do to fix that?
Thanks in advance!
Do you want to raise an exception (which would prevent the insert from succeeding)? Or do you want to allow the insert to succeed and write a string to the dbms_output buffer that may or may not exist and may or may not be shown to a human running the insert?
In either case, you'll want this to be a row-level trigger, not a statement-level trigger, so you'll need to add the for each row clause.
CREATE OR REPLACE TRIGGER person_id_trigg
AFTER INSERT
ON person
FOR EACH ROW
If you want to raise an exception
BEGIN
IF( length( :new.id ) <> 11 )
THEN
RAISE_APPLICATION_ERROR( -20001,
'The new ID value must have a length of 11' );
END IF;
END;
If you want to potentially print output but allow the insert to succeed
BEGIN
IF( length( :new.id ) <> 11 )
THEN
dbms_output.put_line( 'The new ID value must have a length of 11' );
END IF;
END;
Of course, in reality, you would never use a trigger for this sort of thing. In the real world, you would use a constraint.
Oracle APEX. I want to create trigger: if user deletes a row where ENDDATE is null the row won't be deleted overwise it will. This is my script:
CREATE OR REPLACE TRIGGER CHECK_NOT_NULL_
BEFORE DELETE ON CAREER
FOR EACH ROW
BEGIN
IF(OLD.ENDDATE IS NULL)
INSERT INTO CAREER VALUES (OLD.JOBNO, OLD.EMPNO, OLD.STARTDATE, OLD.ENDDATE);
END IF;
END CHECK_NOT_NULL_;
But I have ORA-24344 error. Can you explain why and what should I do to fix it?
Your trigger attempts to re-insert the row if the END_DATE is null. This won't work (you'll get the notorious mutating table error). But anyway, if you want to prevent deletion of the row it's simpler and clearer to simply do that:
CREATE OR REPLACE TRIGGER CHECK_NOT_NULL_
BEFORE DELETE ON CAREER
FOR EACH ROW
BEGIN
IF :OLD.ENDDATE IS NULL THEN
raise_application_error(-20000, 'Cannot delete a row when ENDDATE is null');
END IF;
END CHECK_NOT_NULL_;
This fails the action and tells the user why their action was refused. Silently undoing a user's action is bad practice, because it's mystifying, and mystified users are unhappy and often angry users.
Precede all olds with a colon :, i.e.
CREATE OR REPLACE TRIGGER CHECK_NOT_NULL_
BEFORE DELETE ON CAREER
FOR EACH ROW
BEGIN
IF(:OLD.ENDDATE IS NULL)
INSERT INTO CAREER VALUES (:OLD.JOBNO, :OLD.EMPNO, :OLD.STARTDATE, :OLD.ENDDATE);
END IF;
END CHECK_NOT_NULL_;
Also, I'd suggest you to name all columns you're inserting into, e.g.
insert into career (jobno, empno, startdate, enddate)
values (:old.jobno, :old.empno, :old.startdate, :old.enddate);
This trigger ends up raising an error regardless of the if statement values. I'm basically selecting from the same table the delete is occurring on and it's not liking it.
CREATE OR REPLACE TRIGGER delete_schedules
AFTER DELETE
ON SCHEDULES
FOR EACH ROW
DECLARE
lParentCond schedules.cond_code%type;
lParentActive schedules.active_flag%type;
lError exception;
BEGIN
if :OLD.thread is not null then
select cond_code, active_flag
into lParentCond, lParentActive
from schedules where schedule_seq = :old.thread;
if lParentCond = 'OK' and lParentActive in ('*', 'F') then
raise lError;
end if;
end if;
EXCEPTION
when lError then
raise;
WHEN OTHERS THEN RAISE;
END delete_schedules;
Any ideas of a workaround?
You're probably getting the dreaded 'MUTATING TABLE' error. Oracle doesn't allow us to fetch data from the table on which the trigger is defined in an AFTER trigger - but in this case you don't need to because the 'old' values are already available. Rewrite your trigger as:
CREATE OR REPLACE TRIGGER delete_schedules
AFTER DELETE
ON SCHEDULES
FOR EACH ROW
BEGIN
if :OLD.thread is not null AND
:OLD.COND_CODE = 'OK' and
:OLD.ACTIVE_FLAG in ('*', 'F')
then
RAISE_APPLICATION_ERROR(-20100, 'Invalid combination of COND_CODE and ACTIVE_FLAG');
end if;
END delete_schedules;
This assumes (based on the use of a singleton SELECT in the question) that there's only one row in SCHEDULES for the given THREAD value. If that's not the case there are other work-arounds, including using a COMPOUND TRIGGER.
Best of luck.
I am using Oracle Apex.
I need to create a trigger for checking the no. of stock whether < 0.
If the no. of stock < 0 then insert action is stopped then prompt a alert message to user.
Below coding dose not work. what is wrong of my code? please help.
My coding:
CREATE OR REPLACE TRIGGER cw_service_b4_trigger
BEFORE INSERT ON cw_serviceline
FOR EACH ROW
DECLARE
exist_stock number;
BEGIN
select stock into exist_stock from cw_inventory where inv_id = :new.inv_id;
if (exist_stock - :new.quantity) < 0 then
dbms_output.put_line ('Out of Stock');
return;
end if;
END;
/
You haven't thrown any sort of error. All your trigger does is output a message.
return doesn't cause the INSERT to stop, it just causes the trigger logic to return.
Instead you need to raise an error to cause the insert to fail.
if exist_stock < :new.quantity then
raise_application_error(-20000, 'Out of stock');
end if;
I have a form running in Oracle Forms 6i which has tabular formatted rows that are being populated from a certain table in the database. One column has a [List_Of_Values] property enabled to allow the user to select among possible values.
Some values among the list can only be selected if the user has permission to do that, and I have created a [ WHEN-VALIDATE-ITEM ] trigger to check the permission after the value has been changed. The trigger raises a form_trigger_failure to prevent the user from saving the changes done.
The problem is that if the user gets notified about lack of permission to select the value, then there is no way for the user to know the previous (old) value to select it again, unless the form is cancelled which will cause his other (valid) changes to be lost too.
Here is the code I have written in the trigger
DECLARE
NEW_LOCATION VARCHAR2(100);
BEGIN
NEW_LOCATION := :BLK_MAT_STG_PLACES_PILE.STG_LOC_ID;
IF NEW_LOCATION LIKE 'OH01%' THEN
IF NOT :GLOBAL.USER_ID LIKE 'Admin%' THEN
MESSAGEBOX('You are not authorized to select this value');
/* What can I write to load the old value to this item? */
RAISE FORM_TRIGGER_FAILURE;
END IF;
END IF;
END;
I have tried ROLLBACK but that did not revert the old value to the form. I tried SYNCHRONIZE as well, but that had no effect. Is there any option other than going through the database table again to pull out the value?
BEGIN
IF NEW_LOCATION LIKE 'OH01%' THEN
IF NOT :GLOBAL.USER_ID LIKE 'Admin%' THEN
MESSAGEBOX('You are not authorized to select this value');
/* Return it to the original value that was fetched from database */
:BLK_MAT_STG_PLACES_PILE.STG_LOC_ID :=
get_item_property('BLK_MAT_STG_PLACES_PILE.STG_LOC_ID'
,DATABASE_VALUE);
RAISE FORM_TRIGGER_FAILURE;
END IF;
END IF;
END;
One of my colleagues found the solution to this problem as follows:
Define a Global Parameter in the parameter list (I named it TEMP_LOCATION)
In the PRE-TEXT-ITEM trigger (which is executed before navigating to the item) I wrote
BEGIN
:GLOBAL.TEMP_LOCATION := :BLK_MAT_STG_PLACES_PILE.STG_LOC_ID;
END;
3 Then in the WHEN-VALIDATE-ITEM trigger code that I wrote in this question, I cancelled raising FORM_TRIGGER_FAILURE and simply filled the item with the TEMP_LOCATION
DECLARE
NEW_LOC VARCHAR2(100);
BEGIN
NEW_LOC := :BLK_MAT_STG_PLACES_PILE.STG_LOC_ID;
IF NEW_LOC LIKE 'OH01%' THEN
IF NOT :GLOBAL.USER_ID LIKE 'Admin_%' THEN
MESSAGE('YOU ARE NOT AUTHORIZED TO SELECT THIS VALUE');
:BLK_MAT_STG_PLACES_PILE.STG_LOC_ID := :GLOBAL.TEMP_LOCATION;
/* this solved my problem */
END IF;
END IF;
END;
I thank all of those who tried to help. If someone comes up with a better answer than my own, then I will happily accept it.