update same table with condition in oracle - sql

Unable to execute the trigger, Can anyone explain where i am doing wrong.
CREATE OR REPLACE TRIGGER HK_WS_ED_CT_T1
BEFORE INSERT OR UPDATE OF CI On HK_WS_ED_CT
FOR EACH ROW
BEGIN
IF (:new.CI = CI) THEN
UPDATE HK_WS_ED_CT
SET (TRANS_OTHER, TRANS_OTHER_DESC) =
(SELECT TRANS_OTHER, TRANS_OTHER_DESC
FROM HK_WS_ED_CT
where CI = :NEW.CI and rownum <=1 order by DATE)
end if;
END;

You are updating the table the trigger is on. This is not allowed.
You should put :new.trans_other equal to something and :new.trans_other_desc to something.
The update of the field will be ok then.
If you really want to update all the rows of that same table you should take a look at compound triggers. But I doubt this is what you want.
The main problem in DB triggers is you can't select, insert, update, delete from the table the trigger is firing on. The table is locked at this moment.

Related

Trigger has mutating error when insert

I am having an issue with a trigger. After I add the trigger to my database, when I try to insert a row that the trigger will act on, I get a mutating table error. I don't know how to avoid this. Would someone mind looking at this and tell me what I am doing wrong, and how to rewrite it?
CREATE OR REPLACE TRIGGER ORNG_INV_LINE_TOTAL_TRIGGER
FOR INSERT OR UPDATE ON ORNG_INV_LINE
COMPOUND TRIGGER
AFTER EACH ROW IS
BEGIN
UPDATE ORNG_INVOICE SET Inv_Amount = (SELECT SUM(ORNG_INV_LINE.Inv_Line_Total)
FROM ORNG_INV_LINE
WHERE ORNG_INVOICE.INV_Num = :NEW.INV_Num);
END AFTER EACH ROW;
END ORNG_INV_LINE_TOTAL_TRIGGER;
/
I'm not sure why it is triggering an error. I'm trying to do the action after the update. All I want to do is get the sum for all of the lines of all matching invoice numbers, and write that value in the INVOICE table. Thanks for the help.
Your trigger is written on the table ORNG_INV_LINE for insert or update and again you made a select on the same table while updating ORNG_INVOICE table so trigger is mutating,
to over come this you have to use statement level trigger instead of row level trigger .
i.e after each statement should be there in code instead of after each row.
This can help you.
Here is the solution...apparently everyone is busy with Thanksgiving. Hopefully this helps the next guy.
create or replace TRIGGER ORNG_INV_L_TTL_TRIG
AFTER INSERT OR UPDATE OR DELETE ON ORNG_INV_LINE
BEGIN
UPDATE ORNG_INVOICE SET Inv_Amount = (SELECT SUM(Inv_Line_Total)
FROM ORNG_INV_LINE
WHERE INV_Num = ORNG_INVOICE.INV_Num);
END ORNG_INV_L_TTL_TRIG;
/

How to create mirror table in oracle by using triggers?

I've created a trigger as follows:
create or replace trigger "PASSENGERS_BACKUP_T1"
after
insert or update or delete on "PASSENGERS"
for each row
begin
if :NEW."P_ID" is NOT null then
INSERT INTO PASSENGERS_BACKUP(
PB_ID,
PB_FIRST_NAME,
PB_LAST_NAME,
PB_STREET_ADDRESS1,
PB_STREET_ADDRESS2,
PB_CITY,
PB_STATE,
PB_POSTAL_CODE,
PB_EMAIL,
PB_PHONE_NUMBER1,
PB_PHONE_NUMBER2,
PB_URL,
PB_CREDIT_LIMIT,
PB_TAGS)
VALUES (
:new.P_ID,
:new.P_FIRST_NAME,
:new.P_LAST_NAME,
:new.P_STREET_ADDRESS1,
:new.P_STREET_ADDRESS2,
:new.P_CITY,
:new.P_STATE,
:new.P_POSTAL_CODE,
:new.P_EMAIL,
:new.PHONE_NUMBER1,
:new.PHONE_NUMBER1,
:new.URL,
:new.CREDIT_LIMIT,
:new.TAGS);
end if;
end;
now, when I update​ an existing row in "passengers" table as per the above trigger another new row is getting added in "passengers_backup" table instead I would like to update the existing row whenever an update is done in "passengers" table rows. As, well If I delete a row in "Passengers" table, if that row exists in 'Passengers_backup' table it should also get deleted. How can I acheive this?
Thanks in advance.
For solving your problem you need to use trigger with corresponding SQL statement for each action: insert, update, delete. As variant you can use something like this (Note, I left only two columns from your example for readability, so modify your trigger as you need):
create or replace trigger "PASSENGERS_BACKUP_TIUD"
after insert or update or delete on "PASSENGER"
for each row
begin
if inserting then
insert into "PASSENGER_BACKUP" (pb_id, pb_first_name)
values (:NEW.pb_id, :NEW.pb_first_name);
elsif updating then
update "PASSENGER_BACKUP"
set pb_id=:NEW.pb_id, pb_first_name=:NEW.pb_first_name
where pb_id=:NEW.pb_id;
elsif deleting then
delete from "PASSENGER_BACKUP"
where pb_id=:OLD.pb_id;
end if;
end;
Also you can see work of this trigger in action on SQL Fiddle.

Mutating table exception when selecting max(date column of TABLE_X) in an after update trigger for TABLE_X

I have a trigger somewhat like this (Sorry can't display actual sql because of company rules and this is from a different machine):
create or replace trigger TR_TABLE_X_AU
after update
on TABLE_X
for each row
declare
cursor cursor_select_fk is
select FK_FOR_ANOTHER_TABLE
from TABLE_Y Y, TABLE_Z Z
where :NEW.JOINING_COL = Y.JOINING_COL
and Y.JOINING_COL = Z.JOINING_COL
and :NEW.FILTER_CONDITION_1 = Y.FILTER_CONDITION_1
and :NEW.FILTER_CONDITION_2 = Y.FILTER_CONDITION_2
and :NEW.SOME_DATE_COL = (select max(SOME_DATE_COL)
from TABLE_X
where FILTER_CONDITION_1 = :NEW.FILTER_CONDITION_1
and FILTER_CONDITION_2 = :NEW.FILTER_CONDITION_2)
begin
for rec in cursor_select_fk loop
PCK_SOME_PACKAGE.SOME_PROC(rec.FK_FOR_ANOTHER_TABLE);
end loop;
end TR_TABLE_X_AU;
We resulted to triggers since it is an enhancement. The nested query selecting the max date seems to be the cause of the problem. Changing it to sysdate results to no exceptions. Any idea on how I can get the max date during the execution of the trigger for TABLE_X? Thanks!
EDIT:
Also, it seems similar functions such as count,sum,etc... produces the same error. Anyone knows a workaround to this?
A mutating table is a table that is being modified by an UPDATE,
DELETE, or INSERT statement, or a table that might be updated by the
effects of a DELETE CASCADE constraint.
The session that issued the triggering statement cannot query or
modify a mutating table. This restriction prevents a trigger from
seeing an inconsistent set of data.
Trigger Restrictions on Mutating Tables
Which means, you cannot issue max(some_date_col) on your TABLE_X in a row-level trigger.
Compound trigger could be a possible workaround.

firebird - after insert or update trigger

I have a calculation in my DB need to update "field1" for "table1" after the update trigger.
The problem that updating that field will cause the after update trigger to fire and execute a lengthy procedure and display errors.
please advise how to update the "field1" after the "After update" trigger has been executed and without making the "after update" trigger to execute again.
I know that I can not use NEW with After trigger.
Thanks
One can use a custom locking mechanism based on context variables which prevent from repeating invocation of AFTER UPDATE trigger.
CREATE TRIGGER au FOR table
AFTER UPDATE
POSITION 0
AS
BEGIN
IF RDB$GET_CONTEXT('USER_TRANSACTION', 'MY_LOCK') IS NULL THEN
BEGIN
RDB$SET_CONTEXT('USER_TRANSACTION', 'MY_LOCK', 1);
...
Do your update operations here
...
RDB$SET_CONTEXT('USER_TRANSACTION', 'MY_LOCK', NULL);
END
WHEN ANY DO
BEGIN
RDB$SET_CONTEXT('USER_TRANSACTION', 'MY_LOCK', NULL);
EXCEPTION;
END
END
The obvious answer is to switch to the BEFORE UPDATE trigger, as pointed out by J Cooper... however, if there is some reason you absolutely have to use AFTER UPDATE then you have to set up a flag which tells that the field needs to be recalculated and check it in your trigger. One way to do it would be to set the field to NULL in BEFORE trigger and then check against NULL in AFTER trigger, ie
CREATE TRIGGER bu_trigger BEFORE UPDATE
BEGIN
-- mark field for recalculation if needed
IF(update invalidates the field1 value)THEN
NEW.field1 = NULL;
END
CREATE TRIGGER au_trigger AFTER UPDATE
BEGIN
-- only update if the field is NULL
IF(NEW.field1 IS NULL)THEN
UPDATE table1 SET field1 = ...;
END
But using this technique you probably have to use lot of IF(OLD.field IS DISTINCT FROM NEW.field)THEN checks in triggers to avoid unnessesary updates in your triggers.
simple solution...
fire the update only if the NEW.FIELD1 value is really new, like this:
CREATE TRIGGER au FOR table1
AFTER UPDATE
POSITION 0
AS
DECLARE VARIABLE TMP AS NUMBER(15,5); -- SAME DATATYPE OF FIELD1
BEGIN
-- MAKE YOUR CALCULATION
TMP=CALCULATEDVALUE;
-- UPDATE THE ROW ONLY IF THE VALUES IS CHANGED
IF (TMP<>NEW.FIELD1) UPDATE TABLE1 SET FIELD1=:TMP WHERE KEY=NEW.KEY; -- PAY ATTENTION IF TMP OR NEW.FIELD1 CAN BE NULL. IN THIS CASE USE COALESCE OR A DIFFERENCE COMPARISON
END
Solution: Use BEFORE UPDATE TRIGGER instead of AFTER UPDATE TRIGGER
CREATE TRIGGER some_trigger BEFORE UPDATE ... etc

Trigger to update another field in same table

I want to run a trigger when I update a certain field on the database, so it updates another field (basically we have 2 different unique IDs for each record, so when one is changed, we need to update the other too - yay!)
CREATE OR REPLACE TRIGGER trigger_name ON table AFTER
UPDATE AS
UPDATE table A
SET unique_to_update = NVL(
(SELECT b.unique_to_update_from
FROM table b
WHERE B.other_unique_id = A.unique_id_to_match
), 0);
I have no idea if this works (scared to test it, quite frankly, since I'm certain it'll break things) and even if it did, it'd run on every single update of that table - not just the one field that I wanted.
Any help would be much appreciated, thank you!
Test anything before putting it in production.
Something like this shoud be your trigger:
CREATE OR REPLACE TRIGGER trigger_name
BEFORE UPDATE of unique_id_to_match
ON table
FOR EACH ROW
AS
BEGIN
select
NVL(
(SELECT b.unique_to_update_from
FROM table b
WHERE B.other_unique_id = :new.unique_id_to_match
), 0)
into :new.unique_to_update
FROM dual;
END;
5000 rows x 900 columns is not so big :)
I was afraid it has 10M of rows :)
OK start
create temporary table tmp_my_important_columns on commit delete rows
as
select unique_id_to_match, unique_to_update_from,
other_unique_id , unique_to_update
from table where rownum < 1;
second
CREATE OR REPLACE TRIGGER trigger_before_upd_stmt
BEFORE UPDATE
ON table
AS
BEGIN
insert into
tmp_my_important_columns
select unique_id_to_match, unique_to_update_from,
other_unique_id , unique_to_update
from table;
END;
third,
CREATE OR REPLACE TRIGGER trigger_name
BEFORE UPDATE of unique_id_to_match
ON table
FOR EACH ROW
AS
BEGIN
select
NVL(
(SELECT b.unique_to_update_from
FROM tmp_my_important_columns b
WHERE B.other_unique_id = :new.unique_id_to_match
), 0)
into :new.unique_to_update
FROM dual;
END;
Comments:
After an update, if you update the same rows again, without commit
(which deletes the tmp table), you'll get problems. So, or you commit
after update, or you can add an after update trigger(without for each row) that deletes all, from tmp table, but this is somehow ugly.
You can add indexes on the temporary table.