When does an insert commit with a trigger on the table? - sql

I have a very simple trigger I wrote for a table at work. The problem is when I use 'AFTER INSERT', the trigger locks the table and nothing else can write to it. When I use 'BEFORE INSERT', the trigger works just fine. Both versions of the trigger compile just fine, but only the 'AFTER INSERT' gives me the error when I use a simple insert statement into the 'EVENT_MESSAGE' table. Below is the trigger and error message:
create or replace TRIGGER TWCC_MHE_RERELEASE
AFTER INSERT
ON EVENT_MESSAGE
FOR EACH ROW
DECLARE
m_count number(3);
BEGIN
select count(*) into m_count from event_message where event_id = 7010 and ek_wave_nbr = :new.ek_wave_nbr;
IF :new.event_id = 7010
THEN
insert into TEST_COUNT
(count, wave_nbr, create_date_time)
values
(m_count,:new.ek_wave_nbr, sysdate);
END IF;
END;

A mutating table occurs when a statement causes a trigger to fire and that trigger references the table that caused the trigger. The best way to avoid such problems is to not use triggers :P
Changed the trigger to an after trigger.
Changed it from a row level trigger to a statement level trigger.
Convert to a Compound Trigger.
Modified the structure of the triggers to use a combination of row and statement level triggers.
Made the trigger autonomous with a commit in it.

Related

Does anyone know why this trigger is not working?

create or replace NONEDITIONABLE TRIGGER SetNapomena
BEFORE INSERT
ON stavkafakture
FOR EACH ROW
DECLARE
V_napomena VARCHAR2(20);
BEGIN
EXECUTE IMMEDIATE 'ALTER TRIGGER zabranjeno DISABLE';
SELECT napomena INTO V_napomena
FROM faktura
WHERE brojfakture =:NEW.brojfakture;
:NEW.napomena := V_napomena;
EXECUTE IMMEDIATE 'ALTER TRIGGER zabranjeno ENABLE';
END;
When I insert into a table (in this case stavkafakture) I get this error:
Cannot commit in a trigger
I did some research and added Pragma Autonomous Transaction and commit, but after adding them I try to insert a row in the table it loads forever and never works.
It stays in load forever.
Does anyone know why?
In Oracle DDL statement like CREATE, ALTER, DROP generate an explicit commit.
IE if you issue this kind of statement this does not requires a COMMIT nor a ROLLBACK :
CREATE TABLE MyTable(MyField NUMBER);
This will create the MyTable table and cannot be ROLLBACK.
This is a limitation of Oracle that other RDBMS like PostreSQL doesn't have.

Sql Oracle 12c Trigger

I need some help.
I'm trying to create a Trigger that execute a procedure whenever insert, delete or update operations are done on a specific table.
This is the trigger
CREATE OR REPLACE NONEDITIONABLE TRIGGER NQDI.GAV_TRG
AFTER INSERT or UPDATE or DELETE ON D_GAV
FOR EACH ROW
BEGIN
PRC_FILL_D_GAV(:old.report_name);
END;
Unofortunately, since the trigger starts before any commit has been done and I need to read from the same table, it gives me the 'D_GAV table is being modified can't be read' error.
Besides, the FOR EACH ROW makes the trigger start for every record changed, while I want the trigger to start only at the end, when every update, insert or delete has been committed, but I haven't find a way to preserve the :old.report_name while doing this.
I know I could do what I want with an "up and running process", but I'd like to avoid that. Is there any other solution that I'm overlooking?
You want a compound trigger. After each row event you insert the data into an array. And after the statement you loop through the data and call your procedure.
create or replace trigger nqdi.gav_trg
for insert or update or delete on d_gav compound trigger
type type_table_of_gav is table of d_gav%rowtype;
v_gavs type_table_of_gav := type_table_of_gav();
after each row is
begin
v_gavs.extend(1);
v_gavs(v_gavs.count).report_name := coalesce(:old.report_name, :new.report_name);
end after each row;
after statement is
begin
for i in 1 .. v_gavs.count loop
prc_fill_d_gav(v_gavs(i).report_name);
end loop;
end after statement;
end gav_trg;

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;
/

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

Oracle Trigger on Insert or Delete or Update

Trying to create an Oracle trigger that runs after a table is updated in any way. I've been googling this all morning and came up with this:
CREATE OR REPLACE TRIGGER gb_qty_change
AFTER UPDATE OR INSERT OR DELETE ON F_ITEM_STORE
FOR EACH ROW
DECLARE
v_qty V_AD_ON_HAND%rowtype;
v_isbn TD_ITEM_DESCRIPTION.TD_IDENTIFIER%type;
BEGIN
delete from gb_transaction where gb_tide = :new.ITST_ITEM_TIDE_CODE;
select TD_IDENTIFIER INTO v_isbn from TD_ITEM_DESCRIPTION where TD_TIDE = :new.ITST_ITEM_TIDE_CODE;
select * INTO v_qty from V_AD_ON_HAND where ITST_ITEM_TIDE_CODE = :new.ITST_ITEM_TIDE_CODE;
insert into gb_transaction(gb_tide, gb_isbn, gb_used_on_hand, gb_new_on_hand)
values(:new.ITST_ITEM_TIDE_CODE, v_isbn, v_qty.USED_ON_HAND, v_qty.NEW_ON_HAND);
END;
/
I'm trying to keep it to a single record per TIDE_CODE in the new table.
V_AD_ON_HAND is a view that pulls an inventory count.
gb_transaction is my new table where I'm logging these events
Comparing it to other peoples code it looks like it should run but I'm getting "Warning: Trigger created with compilation errors."
The problem, I believe is with the :new designations for a delete trigger. There is, after all, no NEW value as the record is expunged. You can only access the :OLD values on delete.
if you section the trigger by operation, you can do this.
CREATE OR REPLACE TRIGGER gb_qty_change
AFTER UPDATE OR INSERT OR DELETE ON F_ITEM_STORE
FOR EACH ROW
DECLARE
v_qty V_AD_ON_HAND%rowtype;
v_isbn TD_ITEM_DESCRIPTION.TD_IDENTIFIER%type;
BEGIN
IF INSERTING or UPDATING then
... insert your existing code
ELSE
... do something similar with the :old values for the deleting case
end if;
END;
/
Incidentally, it is generally helpfull if you tell us WHAT the error is, not just that you had one. If compiling via SQL*Plus script, after the forward slash call to compile the trigger after the "end;" statement, add a line that says:
SHOW ERRORS TRIGGER YOUR_TRIGGER_NAME;