Oracle update trigger issue - sql

Question is I have the following trigger to watch my specific column "CHANGED" if its updated to a 1 I would like it to update the same row and the column "CHANGED_DATE" to the current SYSDATE. For some reason I keep getting this error any help would be greatly appreciated.
Error
UPDATE "SYSADM"."SHIP_CALENDAR" SET CHANGED = '1' WHERE ROWID = 'AABCxCAAEAAAKYEAAB' AND ORA_ROWSCN = '6165377066'
ORA-04091: table SYSADM.SHIP_CALENDAR is mutating, trigger/function may not see it
ORA-06512: at "SYSADM.SHIP_CALENDAR_CHANGED", line 5
ORA-04088: error during execution of trigger 'SYSADM.SHIP_CALENDAR_CHANGED'
Trigger
create or replace TRIGGER SHIP_CALENDAR_CHANGED
AFTER UPDATE
OF CHANGED
ON SHIP_CALENDAR
REFERENCING OLD AS o NEW AS n
FOR EACH ROW
DECLARE
BEGIN
-- UPDATE SHIP CALENDAR SET UPDATE_CHANGE TO SYSDATE IF CHANGED CHANGES
IF :n.CHANGED = '1' then
UPDATE SHIP_CALENDAR
SET CHANGED_DATE = SYSDATE
WHERE SHIPMENT_ID = :o.SHIPMENT_ID;
END IF;
END;
Table

Use a before update trigger:
create or replace TRIGGER SHIP_CALENDAR_CHANGED
BEFORE UPDATE
OF CHANGED
ON SHIP_CALENDAR
REFERENCING OLD AS o NEW AS n
FOR EACH ROW
DECLARE
BEGIN
-- UPDATE SHIP CALENDAR SET UPDATE_CHANGE TO SYSDATE IF CHANGED CHANGES
IF :n.CHANGED = '1' then
:n.CHANGED_DATE := SYSDATE
END IF;
END;

Related

how to convert a mutating trigger to a stored procedure

I have the following trigger:
CREATE OR REPLACE TRIGGER planning_trig BEFORE
UPDATE OF planned_remediation_date ON evergreen
FOR EACH ROW
DECLARE
planned_remediation_date DATE;
BEGIN
SELECT
planned_remediation_date
INTO planned_remediation_date
FROM
evergreen
WHERE
hostname = :new.hostname
AND instance_name = :new.instance_name
AND product_home = :new.product_home
AND it_service = :new.it_service
AND sw_name = :new.sw_name;
IF
planned_remediation_date IS NOT NULL
AND planned_remediation_date > trunc(sysdate)
THEN
UPDATE evergreen
SET
planning_status = 'planned';
ELSE
UPDATE evergreen
SET
planning_status = 'overdue';
END IF;
END;
/
After an update of the row in my table evergreen I get this error:
ORA-04091: table PTR.EVERGREEN is mutating, trigger/function may not see it
I believe the error comes from the fact that I'm trying to update the very same table the trigger is firing on. I read that the best way to handle updates in the same table are stored procedures but I'm quite new to oracle and don't know how to achieve that. Another possibility I heard is AUTONOMOUS_TRANSACTION Pragma but not sure either if this applies to my case.
A little more background, the underlying table has a composite unique key (hostname,instance_name,product_home,it_service,sw_name) and I want to update an existing column on this table called planning_status based on the updated value of planned_remediation_date. If that value is not null and greater then today then update with planned else overdue.
Expression planned_remediation_date IS NOT NULL is redundant. planned_remediation_date > trunc(sysdate) is never TRUE when planned_remediation_date is NULL.
Your trigger can be written much shorter:
CREATE OR REPLACE TRIGGER planning_trig
BEFORE UPDATE OF planned_remediation_date ON evergreen
FOR EACH ROW
BEGIN
IF :new.planned_remediation_date > trunc(sysdate) THEN
:new.planning_status := 'planned';
ELSE
:new.planning_status := 'overdue';
END IF;
END;
/
I guess you like to modify the updated row, not other rows in that table. Otherwise you would need a procedure or a COMPOUND trigger

Why does it raise a mutating table error?

I am attenting to create a trigger that before delete a row in the table CLUBS check if the column END_DATE is null, if it's null the row instead of deleting the row it replaces the value with sysdate and if is not null the row doesn't get deleted.
CREATE OR REPLACE TRIGGER TRANSFER_DATA
BEFORE DELETE ON CLUBS
FOR EACH ROW
BEGIN
IF :old.END_DATE IS NULL
THEN UPDATE CLUBS SET END_DATE = SYSDATE;
ELSE
RAISE_APPLICATION_ERROR(-20033, 'No se puede borrar');
END IF;
END;
I've tried this code but it raises an error:
"table %s.%s is mutating, trigger/function may not see it"
How can I make it work?
By the way, I am working on Oracle sqldeveloper
You need to create a view and a INSTEAD OF trigger:
create or replace view v_CLUBS as
SELECT * FROM CLUBS; -- perhaps add 'WHERE END_DATE IS NULL'
CREATE OR REPLACE TRIGGER TRANSFER_DATA
INSTEAD OF DELETE ON V_CLUBS
FOR EACH ROW
BEGIN
IF :old.END_DATE IS NULL THEN
:new.end_date := sysdate;
ELSE
RAISE_APPLICATION_ERROR(-20033, 'No se puede borrar');
END IF;
END;
Processing through a view and an instead of trigger is differently an effective way to go. It seems your prior attempt approached the task incorrectly. A simple view is fully updateble (generally). But in this case you want to intercept the delete and apply the action to the underlying table. Assuming CLUBS is view you process against and CLUBS_TBL is the actual table the trigger you want is:
-- create trigger to process deletes on the view
create or replace trigger del_clubs_isd
instead of delete on clubs
begin
update clubs_tbl
set end_date = sysdate
where id = :old.id;
end del_clubs_isd;
See a full script at fiddle.
Further, as presented the user never needs to know the club is not actually deleted, but just marked to appear that way.

PL/SQL ORACLE: Trigger updates on delete

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;

pl/sql trigger error : exact fetch returns more than requested number of rows

I am having a tough time understanding what is wrong with my pl/sql trigger.
The error is :
Error report -
SQL Error: ORA-01422: exact fetch returns more than requested number of rows
ORA-06512: at "SYSTEM.TRG_LATE_RETURN", line 6
ORA-04088: error during execution of trigger 'SYSTEM.TRG_LATE_RETURN'
01422. 00000 - "exact fetch returns more than requested number of rows"
*Cause: The number specified in exact fetch is less than the rows returned.
*Action: Rewrite the query or change number of rows requested
My trigger is as follows:
create or replace trigger trg_late_return
before update of DETAIL_RETURNDATE, DETAIL_DUEDATE on TY_DETAILRENTAL
declare
temp_date DATE:= SYSDATE;
temp_due_date DATE:= SYSDATE;
BEGIN
select DETAIL_RETURNDATE
into temp_date
from TY_DETAILRENTAL;
select DETAIL_DUEDATE
into temp_due_date
from TY_DETAILRENTAL;
IF temp_date is null
THEN
update TY_DETAILRENTAL
set DETAIL_DAYSLATE=null;
END IF;
if temp_date <> null
THEN
if temp_date = trunc(temp_due_date) + 1
then
update TY_DETAILRENTAL
set DETAIL_DAYSLATE=0;
end if;
if temp_date > trunc(temp_due_date) + 1
then
update TY_DETAILRENTAL
set DETAIL_DAYSLATE = DETAIL_RETURNDATE - DETAIL_DUEDATE;
end if;
end if;
END;
/
New to SQL and PL/SQL so I would appreciate any help.
None of your queries or updates have filters (where clauses) so you are working on the entire table - that can't be what you intended. You are trying to get all of the value of those columns into scalar variables, which can only hold a single value.
The trigger is also against the same table you're querying and updating, which suggests you actually meant this to be (or need this to be) a row level trigger, not a statement level trigger.
That means you need to add for each row to the definition, and instead of requerying and updating the table, you can operate on the new pseudorecord that is available for row level triggers.
You also can't use <> to compare with null; you're already using is null, and the opposite of that is is not null. Although you could also just use else here.
So this might be what you wanted:
create or replace trigger trg_late_return
before update of detail_returndate, detail_duedate
on ty_detailrental
for each row
begin
if :new.detail_returndate is null
then
:new.detail_dayslate := null;
else
if :new.detail_returndate = trunc(:new.detail_duedate) + 1
then
:new.detail_dayslate := 0;
elsif :new.detail_returndate > trunc(:new.detail_duedate) + 1
then
:new.detail_dayslate := :new.detail_returndate - :new.detail_duedate;
end if;
end if;
end;
/
You can refer to the updated columns value with :new when comparing them too, so you don't need the local variable copies of those values. (You can look at the pre-update values with :old too, but that doesn't seem to be needed here).
Changes you make to the new pseudorecord are used when the actual matching row in the database is finally updated.
Read more about the new/old pseudorecords, and about triggers in general.

ORA-04098 is invalid and failed re-validation

I wrote the below trigger and it was compiled successfully.
create or replace trigger after_update_datetable
after update on date_table
for each row
begin
if(TRUNC(:new.end_date) - TRUNC(:new.start_date) > 90) THEN
UPDATE date_table set END_DATE = :old.END_DATE, START_DATE = :old.START_DATE;
END IF;
END;
However when I performed the below update statement I got this error
update date_table set end_date = sysdate, start_date = sysdate-100;
trigger failed -ORA-04098 is invalid and failed re-validation.
Any help is appreciated. Thanks
you are updating the same table from the trigger, that is not allowed :
if(TRUNC(:new.end_date) - TRUNC(:new.start_date) > 90) THEN
UPDATE date_table set END_DATE = :old.END_DATE, START_DATE = :old.START_DATE;
END IF;
you better use a trigger for BEFORE instead of AFTER, and update the values of the record :NEW
see this answer, it will be useful for you