This question already has answers here:
Table is mutating, trigger/function may not see it (stopping an average grade from dropping below 2.5)
(6 answers)
Closed 5 years ago.
I just started learning Trigger. And I want to make a trigger which after insert on test gives user a notice 'insert successfully'.
Here is my code.
create table test (id number, name varchar2(30));
create trigger tr_test
after insert on test
for each row
begin
dbms_output.put_line('insert successfully');
end;
/
insert into test values(1, 'john');
And I get the error like this:
ERROR at line 1:
ORA-04091: table YUFENG.TEST is mutating, trigger/function may not see it
ORA-06512: at "YUFENG.TR_TEST", line 3
ORA-04088: error during execution of trigger 'YUFENG.TR_TEST'
Could anyone point out the error and tell me how to modify the code?
Sorry that I didn't notice that there are lots of simple mistakes which makes the query not able to run. And my original code declares variable in the trigger, but I didn't post it. I think probably that is the reason why I get the error mutating.
CREATE or REPLACE TRIGGER TR_TEST
AFTER INSERT ON TEST
FOR EACH ROW
DECLARE a VARCHAR2(30);
BEGIN
SELECT ID INTO a FROM TEST WHERE ID=:NEW.ID;
DBMS_OUTPUT.PUT_LINE('insert successfully');
END;
/
the output will only be displayed, if you turn it on (SQLPlus, SQLDeveloper):
set serveroutput on
Your code is incomplete. There is a missing ")" on the CREATE TABLE.
The putline is misspelled. It should be PUT_LINE.
It is an extremely bad idea to use DBMS_OUTPUT in a Trigger. A user may be using a client that cannot or does not handle DBMS_OUTPUT (unlike sqlplus)
Replace after with before as below, also the table needs ')' missing.
create table test (id number, name varchar2(30));
create or replace trigger tr_test
before insert on test
for each row
begin
dbms_output.put_line('insert successfully');
end;
/
also its good idea you specific columns in the insert
insert into test (id,name) values(1, 'john')
/
commit
/
Related
I create a trigger with oracle apex. And I created an App with oracle apex. The code is as follows:
CREATE TABLE test1
(
test1data1 INT NOT NULL,
test1data2 INT NOT NULL,
test1key VARCHAR(20) NOT NULL,
PRIMARY KEY (test1key)
);
insert into test
values(10,10,001);
CREATE OR REPLACE EDITIONABLE TRIGGER "TEST1_T1"
AFTER
update on "TEST1"
for each row
begin
update test1
set test1data1 = :new.test1data2-:old.test1data2;
end;
/
ALTER TRIGGER "TEST1_T1" ENABLE
/
I use App Builder on APEX to create a website page. When I change the value of 'test1data2', it shows that
ORA-04091: table WKSP_APEXHENRY.TEST1 is mutating, trigger/function may not see it'.
How to solve this problem? Thank you.
[[screenshot of problem](https://i.stack.imgur.com/GV2Ov.png)](https://i.stack.imgur.com/5Oy4g.png)
I hope the error can disappear and the trigger can run smoothly.
You are not permitted in a trigger to change the table the triggering statement is modifying. That's what it means by mutating.
Instead, change it to a BEFORE update trigger and inside just assign :new.test1data1. No update is required.
e.g.
CREATE OR REPLACE EDITIONABLE TRIGGER "TEST1_T1" BEFORE update on "TEST1"
for each row
begin
:new.test1data1 := :new.test1data2;
end;
This might appear a simple query for most of you, but I am a beginner in Oracle DB.
A table has been created with below script-
CREATE TABLE PLAN_TABLE
(
PL_ID DECIMAL(10,0) PRIMARY KEY NOT NULL
,PL_NAME VARCHAR2(300) DEFAULT NULL
,UPDATED_TS TIMESTAMP DEFAULT SYSDATE NOT NULL
,DELETE_FLAG DECIMAL(10,0) DEFAULT 0 NOT NULL
);
The requirement is to have SYSDATE for UPDATED_TS for any new record inserted into the table and also in case when the DELETE_FLAG is updated to 1. Can it be done by trigger?
The below trigger was created-
CREATE OR REPLACE TRIGGER PT_BEFORE_INSERT_TR
BEFORE INSERT ON PLAN_TABLE
FOR EACH ROW
BEGIN
SELECT SYSDATE INTO :new.UPDATED_TS FROM DUAL;
dbms_output.put_line('Inserted');
END;
/
Below error was encountered while inserting record into the table-
error: ORA-04091: table I60_SCH04.PLAN_TABLE is mutating, trigger/function may not see it
Can you please help in letting me know that where am I committing the mistake? Is there any better way to achieve the requirement based upon INSERT/UPDATE?
The actual error you get is due to the fact that you try to select from a table that you actually are changing. To prevent the issue there are a couple of methods, but in you case things are really simple.
SYSDATE is a function, that you could call directly inside PL/SQL block (which a trigger actually is) and use the value returned to update the set the column value
CREATE OR REPLACE TRIGGER PT_BEFORE_INSERT_TR
BEFORE INSERT ON PLAN_TABLE
FOR EACH ROW
BEGIN
:new.UPDATED_TS := sysdate;
dbms_output.put_line('Inserted');
END;
/
OK, this covers the insert part.
For updating - once again, many options. One could be - change your trigger to BEFORE INSERT OR UPDATE ON PLAN_TABLE.
In this case whenever you issue update or insert - this trigger is fired for each row and updates the date column accordingly.
And of course you could use particular checks available in triggers, something like
IF INSERTING OR UPDATING('DELETE_FLAG') THEN
...
END IF;
and code in the logic you need.
Lets say I have a table as follows--
create table employees
(
eno number(4) not null primary key,
ename varchar2(30),
zip number(5) references zipcodes,
hdate date
);
I've created a trigger using the following code block
create or replace TRIGGER COPY_LAST_ONO
AFTER INSERT ON ORDERS
FOR EACH ROW
DECLARE
ID_FROM_ORDER_TABLE VARCHAR2(10);
BEGIN
SELECT MAX(ORDERS.ONO)INTO ID_FROM_ORDER_TABLE from ORDERS ;
DBMS_OUTPUT.PUT_LINE(ID_FROM_ORDER_TABLE);
INSERT INTO BACKUP_ONO VALUES( VALUE1, VALUE2,VALUE3, ID_FROM_ORDER_TABLE);
END;
The trigger fires after insertion and attempts to read from the table that fired it(logically duhh!) but oracle is giving me an error and asking me to modify the trigger so that it doesnt read the table. Error code-
Error report -
SQL Error: ORA-04091: table TEST1.ORDERS is mutating, trigger/function may not see it
ORA-06512: at "TEST1.COPY_LAST_ONO", line 8
ORA-04088: error during execution of trigger 'TEST1.LOG_INSERT'
04091. 00000 - "table %s.%s is mutating, trigger/function may not see it"
*Cause: A trigger (or a user defined plsql function that is referenced in
this statement) attempted to look at (or modify) a table that was
in the middle of being modified by the statement which fired it.
*Action: Rewrite the trigger (or function) so it does not read that table.
What I'm trying to achieve with this trigger is to copy the last INSERTED ONO (which is a primary key for the ORDER table) immediately to a different table after being INSERTED. What I don't get is, why oracle complaining? The trigger is attempting to read AFTER the insertion!
Ideas? Solution?
MANY THANKS
If you are trying to log the ONO you just inserted, use :new.ono and skip the select altogether:
INSERT INTO BACKUP_ONO VALUES( VALUE1, VALUE2,VALUE3, :new.ono);
I don't believe you can select from the table you are in the middle of inserting into as the commit has not been issued yet, hence the mutating table error.
P.S. Consider not abbreviating. Make it clear for the next developer and call it ORDER_NUMBER or at least a generally accepted abbreviation like ORDER_NBR, whatever your company's naming standards are. :-)
FYI - If you are updating, you can access :OLD.column as well, the value before the update (of course if the column is not a primary key column).
Amplifying #Gary_W's answer:
Oracle does not allow a row trigger (one with FOR EACH ROW in it) to access the table on which the trigger is defined in any way - you can't issue a SELECT, INSERT, UPDATE, or DELETE against that table from within the trigger or anything it calls (so, no, you can't dodge around this by calling a stored procedure which does the dirty work for you - but good thinking! :-). My understanding is that this is done to prevent what you might call a "trigger loop" - that is, the triggering condition is satisfied and the trigger's PL/SQL block is executed; that block then does something which causes the trigger to be fired again; the trigger's PL/SQL block is invoked; the trigger's code modifies another row; etc, ad infinitum. Generally, this should be taken as a warning that your logic is either really ugly, or you're implementing it in the wrong place. (See here for info on the evil of business logic in triggers). If you find that you really seriously need to do this (I've worked with Oracle and other databases for years - I've really had to do it once - and may Cthulhu have mercy upon my soul :-) you can use a compound trigger which allows you to work around these issues - but seriously, if you're in a hole like this your best option is to re-work the data so you don't have to do this.
Best of luck.
Modify your trigger to use PRAGMA AUTONOMOUS_TRANSACTION
create or replace TRIGGER COPY_LAST_ONO
AFTER INSERT ON ORDERS
FOR EACH ROW
DECLARE
ID_FROM_ORDER_TABLE VARCHAR2(10);
PRAGMA AUTONOMOUS_TRANSACTION; -- Modification
BEGIN
.
.
.
I have created a connection in SQL Developer and added several tables to database. Also, I have defined some triggers. They all work well except for one that gives "identifier too long" error. I am aware of 30 character limit, but i can't see what causes this particular error. With this code, I'm trying to implement the Short-circuit keys.
Tables:
Izvestaj (IzvestajID, Datum, Opis, Tekst, PredmerID, NarucilacID, OsobaID, IzvrsilacID)
Predmer (PredmerID, Datum, Naziv, IzvrsilacID, LokacijaID)
Izvrsilac (IzvrsilacID, Naziv)
Italic values represent the primary keys of relations (tables).
Triggers:
create or replace TRIGGER "T_IZM_IZV"
AFTER UPDATE OF IZVRSILACID ON PREDMER
FOR EACH ROW
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
EXECUTE IMMEDIATE ('ALTER TRIGGER'||'T_ZABRANA_IZM_IZV'||'DISABLE');
UPDATE IZVESTAJ SET IZVRSILACID=:NEW.IZVRSILACID
WHERE PREDMERID=:NEW.PREDMERID;
EXECUTE IMMEDIATE ('ALTER TRIGGER'||'T_ZABRANA_IZM_IZV'||'ENABLE');
END;
create or replace TRIGGER "T_ZABRANA_IZM_IZV"
BEFORE UPDATE OF IzvrsilacID ON Izvestaj
FOR EACH ROW
BEGIN
RAISE_APPLICATION_ERROR(-20000, 'Direct altering of IzvrsilacID is forbidden');
END;
I have added some rows to table "Predmer" (Izvrsilac and Izvestaj also have some values inserted) and when I try to change value of column "IzvrsilacID" (foreign key), i get an error:
UPDATE "KORISNIK"."PREDMER" SET IZVRSILACID = '1' WHERE ROWID = 'AAAFBRAABAAALDxAAB' AND ORA_ROWSCN = '675526'
ORA-00972: identifier is too long
ORA-06512: at "KORISNIK.T_IZM_IZV", line 4
ORA-04088: error during execution of trigger 'KORISNIK.T_IZM_IZV'
One error saving changes to table "KORISNIK"."PREDMER":
Row 2: ORA-00972: identifier is too long
ORA-06512: at "KORISNIK.T_IZM_IZV", line 4
ORA-04088: error during execution of trigger 'KORISNIK.T_IZM_IZV'
What could cause this error? I've tried several things, even renaming triggers, tables and columns to one-letter names, but without any success.
P.S. Sorry about naming. I didn't want to translate table and column names to English because it would differ their length, which seems to cause an error in the first place.
These lines:
EXECUTE IMMEDIATE ('ALTER TRIGGER'||'T_ZABRANA_IZM_IZV'||'DISABLE');
EXECUTE IMMEDIATE ('ALTER TRIGGER'||'T_ZABRANA_IZM_IZV'||'ENABLE');
Should look like this:
EXECUTE IMMEDIATE ('ALTER TRIGGER '||'T_ZABRANA_IZM_IZV'||' DISABLE');
EXECUTE IMMEDIATE ('ALTER TRIGGER '||'T_ZABRANA_IZM_IZV'||' ENABLE');
Note the added spaces.
Without the spaces added, you were trying execute:
ALTER TRIGGERT_ZABRANA_IZM_IZVDISABLE
and
ALTER TRIGGERT_ZABRANA_IZM_IZVENABLE
which clearly isn't going to work.
Hope that helps.
I am new to the sql. I want to create a trigger for copying values between tables.
basically, the task I want to finish is forwarding students' message table values to specific staff_maibox
here is the code.
drop trigger forward_msg_to_staff;
create or replace trigger forward_msg_to_staff
update on message
for each row
declare
message_id VARCHAR2(10);
client_id NUMBER(10);
staff_id NUMBER(5);
message_date DATE;
message_title VARCHAR2(20);
staff_mailbox VARCHAR2(255);
begin
insert into staff_mailbox(message_id, client_id, staff_id, message_date, message_title, staff_mailbox)
values(:new.message_id, :new.client_id, :new.staff_id, :sysdate, :new.message_title, :old.staff_mailbox)
end;
/
is this code correct?
Please advise. thanks in advance.
You're getting an error because you're missing either the BEFORE or AFTER keyword from the CREATE TRIGGER statement.
These are required as indicated in the documentation:
Additionally:
There's no need to declare all the variables, you're not using them
:sysdate is incorrect, you're not binding it. You can just use sysdate instead as you would in standard SQL or PL/SQL.
You're missing a semi-colon after the VALUES clause of the INSERT statement.
Putting this together your trigger may look like this
create or replace trigger forward_msg_to_staff
after update on message
for each row
begin
insert into staff_mailbox( message_id, client_id, staff_id, message_date
, message_title, staff_mailbox )
values ( :new.message_id, :new.client_id, :new.staff_id, sysdate
, :new.message_title, :old.staff_mailbox );
end forward_msg_to_staff;
/
Note that I've used the trigger name in the END as well. This is for convenience only, it makes it obvious where the trigger ends...
If you want to see what errors your're getting when you're creating a trigger use show errors as a_horse_with_no_name suggests. This shows any compilation errors, which is invaluable for tracking them down.