An "After Insert" oracle trigger stops any inserts that meet a certain condition. The trigger is checking the column "CLASS_TIME" and if it is less than 9 am or greater than 6 pm then update the inserted row. But the row is not inserted altogether hence its not possible to update the same row. Below is the trigger code
create or replace TRIGGER ENFORCE_CLASS_TIMINGS
AFTER INSERT ON SYSTEM.TUTPRAC
REFERENCING OLD AS OLD1 NEW AS NEW1
FOR EACH ROW
WHEN (
to_number(to_char(to_date(NEW1.CLASS_TIME,'hh24:mi'),'sssss')) <
to_number(to_char(to_date('09:00', 'hh24:mi'), 'sssss')) OR
to_number(to_char(to_date(NEW1.CLASS_TIME,'hh24:mi'),'sssss')) >
to_number(to_char(to_date('18:00', 'hh24:mi'), 'sssss')))
BEGIN
UPDATE SYSTEM.TUTPRAC
SET STAFFNO = NULL
WHERE
CLASSID = :NEW1.CLASSID;
COMMIT;
END;
Please suggest how to fix it.
Try this (I removed REFERENCING OLD as OLD1 as it has no meaning in an insert trigger):
create or replace TRIGGER ENFORCE_CLASS_TIMINGS
BEFORE INSERT ON SYSTEM.TUTPRAC
REFERENCING NEW AS NEW1
FOR EACH ROW
WHEN (
to_number(to_char(to_date(:NEW1.CLASS_TIME,'hh24:mi'),'sssss')) <
to_number(to_char(to_date('09:00', 'hh24:mi'), 'sssss')) OR
to_number(to_char(to_date(:NEW1.CLASS_TIME,'hh24:mi'),'sssss')) >
to_number(to_char(to_date('18:00', 'hh24:mi'), 'sssss')))
BEGIN
:NEW1.STAFFNO := NULL;
END;
Related
Good Day Buddies!
So, here is my Question, it says -
Write a update, delete trigger on clientmstr table. The System
should keep track of the records that ARE BEING updated or
deleted. The old value of updated or deleted records should be
added in audit_trade table. (Separate implementation using both row
and statement triggers)
And my solution looks like this -
-- For row trigger
create or replace trigger row_trigger
before delete or update on client_master
referencing old as old new as new
for each row
begin
insert into audit_table values(
:old.client_id, :old.client_name, :old.client_budget
);
end;
/
And as per the question I have to implement the same using statement trigger but I couldn't think of a way it can be done. I studied about statement triggers and I learned that we can't use :old and :new here. Is there any way we can implement the same row trigger method of adding in audit table using statement trigger? I am just starting out and it's just been two days I started learning PL/SQL. I spend whole day searching everywhere on the internet - tried looking for an example but I am not getting it. Can anyone help?
Edit
(1) I am using Oracle SQL Developer
(2) As someone suggested in comments - it isn't possible to do this in statement trigger, I think the same. I have to submit my assignment this Saturday. I had a conversation with my teacher - she said it's possible to implement it using statement trigger. I asked her how - but she didn't responded. Then I asked her for a hint and she said this (I'm copy pasting her text)-
Create a separate table with col as operations and timestamp. Write statement level trigger on insert update and delete operations. The trigger will capture the operation fired and timestamp by inserting values in table.
I am not getting what does that mean or how to do it! Can anyone help me solve this?
You could use a compound trigger.
Create the types:
CREATE TYPE client_master_obj IS OBJECT(
id NUMBER,
name VARCHAR2(20),
budget NUMBER(10,2)
);
CREATE TYPE client_master_table IS TABLE OF client_master_obj;
Then the trigger:
CREATE TRIGGER client_master_cmp_trigger
FOR DELETE OR UPDATE ON client_master
COMPOUND TRIGGER
data client_master_table := client_master_table();
AFTER EACH ROW
IS
BEGIN
data.EXTEND(1);
data(data.COUNT) := client_master_obj(
:OLD.client_id,
:OLD.client_name,
:OLD.client_budget
);
END AFTER EACH ROW;
AFTER STATEMENT
IS
BEGIN
INSERT INTO audit_table (client_id, client_name, client_budget, trg_type)
SELECT id,
name,
budget,
'C'
FROM TABLE(data);
END AFTER STATEMENT;
END;
/
Which, for the sample data:
CREATE TABLE client_master (client_id, client_name, client_budget) AS
SELECT 1, 'Alice', 100 FROM DUAL UNION ALL
SELECT 2, 'Beryl', 200 FROM DUAL UNION ALL
SELECT 3, 'Carol', 300 FROM DUAL UNION ALL
SELECT 4, 'Debra', 400 FROM DUAL UNION ALL
SELECT 5, 'Emily', 500 FROM DUAL;
CREATE TABLE audit_table (client_id, client_name, client_budget, trg_type) AS
SELECT cm.*, 'X' FROM client_master cm WHERE 1 = 0;
Then after:
UPDATE client_master
SET client_budget = client_budget + 600
WHERE client_id IN (1, 2);
DELETE FROM client_master WHERE client_id IN (1, 3);
Then the audit table contains (with the row trigger also firing for the same changes):
SELECT * FROM audit_table;
CLIENT_ID
CLIENT_NAME
CLIENT_BUDGET
TRG_TYPE
1
Alice
100
R
2
Beryl
200
R
1
Alice
100
C
2
Beryl
200
C
1
Alice
700
R
3
Carol
300
R
1
Alice
700
C
3
Carol
300
C
db<>fiddle here
Same approach using compound trigger, but although is not literally a level statement trigger, because normally they refer to table level triggers.
create or replace trigger row_compound_trigger
for delete or update on client_master
compound trigger
--
-- an array structure to buffer all the row changes
--
type t_row_list is
table of client_master%rowtype index by pls_integer;
l_audit_rows t_row_list;
l_operation varchar2(1) :=
case when updating then 'U'
when deleting then 'D'
end;
before statement is
begin
--
-- initialize the array
--
l_audit_rows.delete;
end before statement;
after each row is
begin
--
-- at row level, capture all the changes into the array
-- this variables use sys_context in case you want to use it ( not needed )
--
l_audit_rows(l_audit_rows.count+1).aud_who := sys_context('USERENV','SESSION_USER');
l_audit_rows(l_audit_rows.count).aud_when := sysdate;
l_audit_rows(l_audit_rows.count).aud_operation := l_operation;
l_audit_rows(l_audit_rows.count).aud_module := sys_context('USERENV','MODULE');
if updating then
l_audit_rows(l_audit_rows.count).client_id := :new.client_id
l_audit_rows(l_audit_rows.count).client_name := :new.client_name
... all the fields
else
l_audit_rows(l_audit_rows.count).client_id := :old.client_id
l_audit_rows(l_audit_rows.count).client_name := :old.client_name
... all the fields
end if;
end after each row;
after statement is
begin
--
-- then at completion, do a single insert of all the rows into our audit table
--
forall i in 1 .. l_audit_rows.count
insert into audit_table values l_audit_rows(i);
l_audit_rows.delete;
end after statement;
end;
/
Hi I try to Insert value in the second trigger with new id from first trigger only if condition is fulfiled, but I'm stuck.
table1_trg works
CREATE TABLE table1 (
id NUMBER(9,0) NOT NULL,
subject VARCHAR2(200) NOT NULL,
url_address VARCHAR2(200) NOT NULL,
)
CREATE OR REPLACE TRIGGER table1_trg
BEFORE INSERT ON table1
FOR EACH ROW
BEGIN
SELECT table1_seq.NEXTVAL
INTO :new.id
FROM dual;
END;
/
CREATE OR REPLACE TRIGGER table1_url
BEFORE INSERT ON table1
FOR EACH ROW
WHEN (NEW.subject = 'Task')
BEGIN
INSERT INTO CSB.table1 (url_address)
VALUES ('blabla.com?' || :new.id);
END;
/
I insert only subject but after that i receive exception that subject can not be null.
INSERT INTO corp_tasks_spec (subject) VALUES ('Task')
Any ideas how to resolve it?
You should not be inserting a new record into the same table, you should be modifying the column values for the row you're already inserting - which the trigger is firing against. You're getting the error because of that second insert - which is only specifying the URL value, not the subject or ID (though the first trigger would fire again and set the ID for that new row as well - so it complains about the subject).
Having two triggers on the same firing point can be difficult in old versions of Oracle as the order they fired wasn't guaranteed - so for instance your second trigger might fire before the first, and ID hasn't been set yet. You can control the order in later versions (from 11g) with FOLLOWS:
CREATE OR REPLACE TRIGGER table1_url
BEFORE INSERT ON table1
FOR EACH ROW
FOLLOWS table1_trg
WHEN (NEW.subject = 'Task')
BEGIN
:NEW.url_address := 'blabla.com?' || :new.id;
END;
/
This now fires after the first trigger, so ID is set, and assigns a value to the URL in this row rather than trying to create another row:
INSERT INTO table1 (subject) VALUES ('Task');
1 row inserted.
SELECT * FROM table1;
ID SUBJECT URL_ADDRESS
---------- ---------- --------------------
2 Task blabla.com?2
But you don't really need two triggers here, you could do:
DROP TRIGGER table1_url;
CREATE OR REPLACE TRIGGER table1_trg
BEFORE INSERT ON table1
FOR EACH ROW
BEGIN
:NEW.id := table1_seq.NEXTVAL; -- no need to select from dual in recent versions
IF :NEW.subject = 'Task' THEN
:NEW.url_address := 'blabla.com?' || :new.id;
END IF;
END;
/
Then that trigger generates the ID and sets the URL:
INSERT INTO table1 (subject) VALUES ('Task');
1 row inserted.
SELECT * FROM table1;
ID SUBJECT URL_ADDRESS
---------- ---------- --------------------
2 Task blabla.com?2
3 Task blabla.com?3
Of course, for anything except Task you'll have to specify the URL as part of the insert, or it will error as that is a not-null column.
Create sequence
CREATE SEQUENCE table1_SEQ
START WITH 1
MAXVALUE 100000
MINVALUE 1
NOCYCLE
NOCACHE
NOORDER;
CREATE TRIGGER
CREATE OR REPLACE TRIGGER table1_TRG
Before Insert
ON table1 Referencing New As New Old As Old
For Each Row
Declare V_Val Number;
Begin
Select table1_SEQ.NextVal into V_Val From Dual;
If Inserting Then
:New.id:= V_Val;
End If;
End;
/
I am trying to make a trigger that increases the booked value and decreases the available value whenever new record is inserted inside the table ticket_price. If a record is deleted, I want it to decrease the booked value and increase the available value.
Although I am able to successfully make the trigger work for INSERT, I am unable to do the same for updating the values on deletion of a record.T his is the error I get whenever I try to delete a record
ORA-01403: no data found
ORA-06512: at "K.CAL", line 6
ORA-04088: error during execution of trigger 'K.CAL'
Just to clarify, I am updating values in another table, not the same table I am deleting!
Here is my code for the trigger:
CREATE OR REPLACE TRIGGER cal
BEFORE INSERT OR DELETE ON TICKET_PRICE FOR EACH ROW
DECLARE
V_TICKET TICKET_PRICE.TICKETPRICE%TYPE;
V_BOOKED FLIGHTSEAT.BOOKED_SEATS%TYPE;
V_AVAILABLE FLIGHTSEAT.AVAILABLE_SEATS%TYPE;
BEGIN
SELECT BOOKED_SEATS,AVAILABLE_SEATS
INTO V_BOOKED,V_AVAILABLE
FROM FLIGHTSEAT
WHERE SEAT_ID=:NEW.SEAT_ID;
IF INSERTING THEN
V_BOOKED:=V_BOOKED+1;
V_AVAILABLE:=V_AVAILABLE-1;
UPDATE FLIGHTSEAT
SET BOOKED_SEATS=V_BOOKED, AVAILABLE_SEATS=V_AVAILABLE
WHERE SEAT_ID=:NEW.SEAT_ID;
ELSIF DELETING THEN
V_BOOKED:=V_BOOKED-1;
V_AVAILABLE:=V_AVAILABLE+1;
UPDATE FLIGHTSEAT
SET BOOKED_SEATS=V_BOOKED, AVAILABLE_SEATS=V_AVAILABLE
WHERE SEAT_ID=1;
END IF;
END;
You have correctly surmised that :new.seat is not available on the update for a delete. But neither is it available for the select and ow could you know sea_id=1 was need to be updated? For reference to Deleted row data use :Old.column name; is this case use :old_seat_id for both select and update.
But you don't need the select at all. Note: Further you have an implied assumption that seat_id is unique. I'll accept that below.
create or replace trigger cal
before insert or delete on ticket_price
for each row
declare
v_seat_id flightseat.seat_id%type;
v_booked flightseat.booked_seats%type;
begin
if INSERTING then
v_booked := 1;
v_seat_id := :new.seat_id;
else
v_booked := -1;
v_seat_id := :old.seat_id;
end if;
update flightseat
set booked_seats=booked_seats+v_booked
, available_seats=available_seats-v_booked
where seat_id = v_seat_id;
end;
I want a trigger that updates the value of a column, but I just want to update a small set of rows that depends of the values of the inserted row.
My trigger is:
CREATE OR REPLACE TRIGGER example
AFTER INSERT ON table1
FOR EACH ROW
BEGIN
UPDATE table1 t
SET column2 = 3
WHERE t.column1 = :new.column1;
END;
/
But as I using FOR EACH ROW I have a problem when I try it, I get the mutating table runtime error.
Other option is not to set the FOR EACH ROW, but if I do this, I dont know the inserted "column1" for comparing (or I dont know how to known it).
What can I do for UPDATING a set of rows that depends of the last inserted row?
I am using Oracle 9.
You should avoid the DML statements on the same table as defined in a trigger. Use before DML to change values of the current table.
create or replace trigger example
before insert on table1
for each row
begin
:new.column2 := 3;
end;
/
You can modify the same table with pragma autonomous_transaction:
create or replace trigger example
after insert on table1 for each row
declare
procedure setValues(key number) is
pragma autonomous_transaction;
begin
update table1 t
set column2 = 3
where t.column1 = key
;
end setValues;
begin
setValues(:new.column1);
end;
/
But I suggest you follow #GordonLinoff answere to your question - it's a bad idea to modify the same table in the trigger body.
See also here
If you need to update multiple rows in table1 when you are updating one row, then you would seem to have a problem with the data model.
This need suggests that you need a separate table with one row per column1. You can then fetch the value in that table using join. The trigger will then be updating another table, so there will be no mutation problem.
`create table A
(
a INTEGER,
b CHAR(10)
);
create table B
(
b CHAR (10),
d INTEGER
);
create trigger trig1
AFTER INSERT ON A
REFERENCING NEW AS newROW
FOR EACH ROW
when(newROW.a<=10)
BEGIN
INSERT into B values(:newROW.b,:newROW.a);
END trig1;
insert into A values(11,'Gananjay');
insert into A values(5,'Hritik');
select * from A;
select * from B;`
Hi guys I have two triggers I am meant to be be creating but I am getting compilation errors on both
this first is supposed to record evaluations of 0 to an audit table and the second is supposed to prevent the deletion of entries in which the date is less than todays date.
SQL> CREATE TABLE EVALUATION_AUDIT
2 (C_NAME VARCHAR (15), CO_ID NUMBER(7), E_DATE DATE,
3 V_ID NUMBER (7), C_EVALUATION NUMBER(1));
Table created.
SQL> CREATE OR REPLACE TRIGGER ZERO_EVAL
2 BEFORE INSERT OR UPDATE OF C_EVALUATION ON CUSTOMER_EVENT
3 FOR EACH ROW
4 WHEN (NEW.C_EVALUATION = 0)
5 BEGIN
6 SELECT C_NAME, CO_ID, E_DATE, V_ID, C_EVALUATION
7 FROM CUSTOMER_EVENT CE, CUSTOMER C, EVENT E
8 WHERE CE.C_ID = C.C_ID
9 AND CE.EVENT_ID = E.EVENT_ID
10 AND C_EVALUATION = NEW.C_EVALUATION;
11 INSERT INTO EVALUATION AUDIT
12 VALUES (:NEW.C_NAME, :NEW.CO_ID, :NEW.E_DATE, :NEW.V_ID, :NEW.C_EVALUATION);
13 END;
14 /
Warning: Trigger created with compilation errors.
SQL> CREATE OR REPLACE TRIGGER PASTEVENTS
2 BEFORE DELETE
3 ON EVENT
4 FOR EACH ROW
5 BEGIN
6 IF :OLD.E_DATE =< SYSDATE
7 THEN RAISE_APPLICATION_ERROR (-20002, 'CAN NOT DELETE PAST EVENT RECORDS');
8
9 END IF;
10 END;
11 /
Warning: Trigger created with compilation errors.
As Justin said, when you get created with compilation errors for any stored PL/SQL, type show errors, or you can query the user_errors table to see all outstanding errors on your objects.
From a quick scan, the first trigger is missing a colon when you reference NEW.C_EVALUATION in the select:
AND C_EVALUATION = :NEW.C_EVALUATION;
You need to select into something, though I'm not sure if it's necessary here as you have the values from the :NEW psuedorecord; not sure why you're selecting at all?
And the second has an incorrect operator, =< instead of <=:
IF :OLD.E_DATE <= SYSDATE
It's generally a good idea to prefix column names with the table alias to avoid ambiguity, e.g. SELECT C.C_NAME, ... if that column comes from the CUSTOMER table, etc. You could have another error in there is you have the same column on multiple tables. And it's good practise to list the column names in your INSERT too, i.e. INSERT INTO EVALUATION_AUDIT (C_NAME, ...) VALUES (...). With the missing underscore that #Dba spotted!
In your first trigger code you don't need to SELECT from the tables as you are just inserting the values from the table CUSTOMET_EVENT to EVALUATION_AUDIT. Also you have missed and underscore _ in table_name in EVALUATION_AUDIT in line 11.
CREATE OR REPLACE TRIGGER zero_eval
BEFORE INSERT OR UPDATE OF c_evaluation ON customer_event
FOR EACH ROW
WHEN (NEW.c_evaluation = 0)
BEGIN
INSERT INTO evaluation_audit(c_name, co_id, e_date, v_id, c_evaluation)
VALUES (:NEW.c_name, :NEW.co_id, :NEW.e_date, :NEW.v_id, :NEW.c_evaluation);
END;
/
In Your second code, it should be <= instead of =<
CREATE OR REPLACE TRIGGER pastevents
BEFORE DELETE
ON event
FOR EACH ROW
BEGIN
IF :OLD.e_date <= SYSDATE THEN
raise_application_error (-20002, 'CAN NOT DELETE PAST EVENT RECORDS');
END IF;
END;
/