Following is a dummy table on which i am performing some updations. Here, i am only manipulating the "NAME" column. What i need is to insert the affected row, (say) i change the second for for "ID"-2, then i need that only this row should get inserted in a new table with new / updated value. I have tried doing so via a trigger but it vomits an error regarding table mutation.
ID NAME
================
1 Vaz
2 Dan
3 Steve
The table i want the data to get inserted has the same structure as the above mentioned table and same columns with same datatype.
Please suggest if this could be done in any other way or I am writing wrong code for the trigger. Here is the code i wrote for the purpose :
CREATE OR REPLACE TRIGGER HR.ins_temp
after UPDATE OF name ON HR.TEMP2 FOR EACH ROW
BEGIN
INSERT INTO temp3
(SELECT NAME
FROM temp2
WHERE (:new.NAME<>:old.NAME));
END;
temp2 is the manipulation tabel and temp3 is the new one.
No need for a SELECT. And you probably also want to put the ID value into the table temp3
CREATE OR REPLACE TRIGGER HR.ins_temp
after UPDATE OF name ON HR.TEMP2 FOR EACH ROW
BEGIN
INSERT INTO temp3 (id, name)
values (:new.id, :new.name);
END;
/
And as the trigger is defined as update of name you don't really need to check if the name changed, but if you want, you can do:
CREATE OR REPLACE TRIGGER HR.ins_temp
after UPDATE OF name ON HR.TEMP2 FOR EACH ROW
BEGIN
if ( (:new.name <> :old.name)
OR (:new.name is null and :old.name is not null)
OR (:new.name is not null and :old.name is null))
then
INSERT INTO temp3 (id, name)
values (:new.id, :new.name);
end if;
END;
/
Related
create trigger calculation after insert on employee
for each row
begin
if :new.updated_sal is null
then
update employee set updated_sal= (10/100)* salary
where id=:new.id;
end if;
end;
I would like to create a trigger on the employee table, whenever a new record is inserted in the same table, a 10% of salary in the salary column should be calculated and put into another column updated_sal.
If I try to insert a new record, it is showing that the table is mutated, etc
It's just the :new pseudorecord you need:
create trigger calculation
after insert on employee
for each row
begin
if :new.updated_sal is null then
:new.updated_sal := (10/100) * :new.salary;
end if;
end;
You need to use before insert trigger,
And use :New.updated_sal:= :new.salary * somevalue
To assign salary.
As discussed here, I'm unable to use :OLD and :NEW on columns with collation other than USING_NLS_COMP. I'm trying to find a way around this but haven't been successful so far.
This is the original trigger:
CREATE OR REPLACE TRIGGER SYS$PERSONSSALUTATIONAU
AFTER UPDATE ON PERSONS
FOR EACH ROW
begin
State_00.Salutations_ToDelete(State_00.Salutations_ToDelete.Count + 1) := :old.SalutationTitle;
State_00.Salutations_ToInsert(State_00.Salutations_ToInsert.Count + 1) := :new.SalutationTitle;
end;
This is what I've tried:
CREATE OR REPLACE TRIGGER SYS$PERSONSSALUTATIONAU
FOR UPDATE ON Persons
COMPOUND TRIGGER
TYPE Persons_Record IS RECORD (
SalutationTitle NVARCHAR2(30)
);
TYPE Persons_Table IS TABLE OF Persons_Record INDEX BY PLS_INTEGER;
gOLD Persons_Table;
gNEW Persons_Table;
BEFORE EACH ROW IS BEGIN
SELECT SalutationTitle
BULK COLLECT INTO gOLD
FROM Persons
WHERE ID = :OLD.ID;
END BEFORE EACH ROW;
AFTER EACH ROW IS BEGIN
SELECT SalutationTitle
BULK COLLECT INTO gNEW
FROM Persons
WHERE ID = :NEW.ID;
END AFTER EACH ROW;
AFTER STATEMENT IS BEGIN
FOR i IN 1 .. gNEW.COUNT LOOP
State_00.Salutations_ToDelete(State_00.Salutations_ToDelete.Count + 1) := gOLD(i).SalutationTitle;
State_00.Salutations_ToInsert(State_00.Salutations_ToInsert.Count + 1) := gNEW(i).SalutationTitle;
END LOOP;
END AFTER STATEMENT;
END;
This results in error ORA-04091. I've also tried moving the select into the AFTER STATEMENT section which works, but there is no way to access the old values. If somebody has a solution for this it would be most appreciated.
EDIT:
I created a minimal reproducible example:
CREATE TABLE example_table (
id VARCHAR2(10),
name NVARCHAR2(100)
);
CREATE TABLE log_table (
id VARCHAR2(10),
new_name NVARCHAR2(100),
old_name NVARCHAR2(100)
);
CREATE OR REPLACE TRIGGER example_trigger
AFTER UPDATE ON example_table
FOR EACH ROW BEGIN
INSERT INTO log_table VALUES(:old.id, :new.name, :old.name);
END;
INSERT INTO example_table VALUES('01', 'Daniel');
-- this works as expected
UPDATE example_table SET name = ' John' WHERE id = '01';
SELECT * FROM log_table;
DROP TABLE example_table;
CREATE TABLE example_table (
id VARCHAR2(10),
-- this is the problematic part
name NVARCHAR2(100) COLLATE XCZECH_PUNCTUATION_CI
);
INSERT INTO example_table VALUES('01', 'Daniel');
-- here nothing is inserted into log_example, if you try to
-- recompile the trigger you'll get error PLS-00049
UPDATE example_table SET name = ' John' WHERE id = '01';
SELECT * FROM log_table;
DROP TABLE example_table;
DROP TABLE log_table;
DROP TRIGGER example_trigger;
In the discussion you reference a document concerning USING_NLS_COMP. That has nothing to do with the error you are getting. The error ORA-04091 is a reference to the table that fired the trigger (mutating). More to come on this. I am not saying you do not have USING_NLS_COMP issues, just that they are NOT causing the current error.
There are misconceptions shown in your trigger. Beginning with the name itself; you should avoid the prefix SYS. This prefix is used by Oracle for internal objects. While SYS prefix is not specifically prohibited at best it causes confusion. If this is actually created in the SYS schema then that in itself is a problem. Never use SYS schema for anything.
There is no reason to create a record type containing a single variable, then create a collection of that type, and finally define variables of the collection. Just create a collection to the variable directly, and define variables of the collection.
The bulk collect in the select statements is apparently misunderstood as used. I assume you want to collect all the new and old values in the collections. Bulk collect however will not do this. Each time bulk collect runs the collection used is cleared and repopulated. Result being the collection contains only the only the LAST population. Assuming id is unique the each collection would contain only 1 record. And now that brings us to the heart of the problem.
The error ORA-04091: <table name> is mutating, trigger/function may not see it results from attempting to SELECT from the table that fired the trigger; this is invalid. In this case the trigger fired due to a DML action on the persons table as a result you cannot select from persons in a row level trigger (stand alone or row level part of a compound trigger. But it is not needed. The pseudo rows :old and :new contain the complete image of the row. To get a value just reference the appropriate row and column name. Assign that to your collection.
Taking all into account we arrive at:
create or replace trigger personssalutation
for update
on persons
compound trigger
type persons_table is table of
persons.salutationtitle%type;
gold persons_table := persons_table();
gnew persons_table := persons_table();
before each row is
begin
gold.extend;
gold(gold.count) := :old.salutationtitle;
end before each row;
after each row is
begin
gnew.extend;
gold(gold.count) := :new.salutationtitle;
end after each row;
after statement is
begin
for i in 1 .. gnew.count loop
state_00.salutations_todelete(state_00.salutations_todelete.count + 1) := gold(i);
state_00.salutations_toinsert(state_00.salutations_toinsert.count + 1) := gnew(i);
end loop;
end after statement;
end personssalutation;
NOTE: Unfortunately you did not provide sample data, nor description of the functions in the AFTER STATEMENT section. Therefore the above is not tested.
I'm going to create a trigger to audit a table. Suppose my table define as below.
COUNTRY_ID NOT NULL CHAR(2)
COUNTRY_NAME VARCHAR2(40)
REGION_ID NUMBER
and my log table created as below.
create table country_log(
username varchar2(10),
transaction_date date,
new_value varchar(20),
old_value varchar(20)
)
My half completed trigger would be like below.
CREATE OR REPLACE TRIGGER tr_countryTable
AFTER UPDATE ON COUNTRIES
FOR EACH ROW
BEGIN
insert into country_log (username,transaction_date,new_value,old_value ) values (USER, sysdate,**:New, :Old** );
END;
/
I need to know instead of comparing each column value in :old and :new, how to get exactly updated column's new and old values.
In an UPDATE trigger, a column name can be specified with an UPDATING predicate to determine if the named column is being updated. Let's define a trigger as the following:
CREATE OR REPLACE TRIGGER tr_countryTable
AFTER UPDATE ON COUNTRIES
FOR EACH ROW
BEGIN
IF UPDATING ('COUNTRY_NAME') THEN
-- :new.COUNTRY_NAME is new value
-- :old.COUNTRY_NAME is old value
END IF;
IF UPDATING ('REGION_ID') THEN
-- :new.REGION_ID is new value
-- :old.REGION_ID is old value
END IF;
END;
/
Oracle 11g triggers documentation
So I am trying to add age with price and store the result on another table with the ID of the person who did this. I am able to set the business rule by making the trigger but when I check my second (END) table, there is nothing there.. Here is my code for the trigger:
CREATE OR REPLACE TRIGGER JIM
BEFORE INSERT ON END
FOR EACH ROW ENABLE
DECLARE
V_AGE JIM.AGE%TYPE;
V_PRICE JIM.PRICE%TYPE;
v_prices NUMBER(20);
BEGIN
SELECT AGE,PRICE INTO V_AGE,V_PRICE FROM JIM WHERE ID=:NEW.ID;
v_prices:=V_AGE+V_PRICE;
INSERT INTO END VALUES(:new.ID,v_prices);
END;
However, When I insert values onto the JIM table using the following code:
insert into jim values(4,'Sim',45,100);
nothing actually gets stored on the END table. i am sort of new to triggers and its so confusing. Please let me know what to do. thanls
Don't use a keyword end as a table name, this causes problem during
creation of trigger. I've presumed the table's name as t_end.
I think you are confused on which table to define trigger. It seems
you should define on table jim instead of t_end.
I've presumed you have a sequence named seq_end to populate the id
column of the table t_end
So , your trigger creation statement will be as follows :
create or replace trigger trg_ins_jim before insert on jim for each row
declare
v_id_end t_end.id%type;
v_prices t_end.prices%type;
begin
v_prices := :new.age + :new.price;
v_id_end := seq_end.nextval; insert into t_end values(v_id_end, v_prices);
/* if you have defined sequence for t_end as default value of id column, you may change the upper row as "insert into t_end(prices) values(v_prices);" and there would be no need for "v_id_end" */
end;
and when you issue insert into jim values(4,'Sim',45,100); command, you'll also have values inserted into t_end.
If there is only one table, then the SELECT and the INSERT are redundant.
CREATE OR REPLACE TRIGGER trigger_name
BEFORE INSERT ON table_name
FOR EACH ROW
BEGIN
:NEW.PRICES := :NEW.AGE + :NEW.PRICE;
END;
Possible duplicate
I have a function called my_func , I would like to pass one of the field of the latest row inserted and update another field in that same row with the value returned from function.
My DDL is,
create table sampletable ( indexid number(19,0) , xmlcolumn xmltype not null , checkfield raw(30) null) xmltype column xmlcolumn store as binary xml;
This is my insert query
insert into sample_table values ( 1 , some_xml_document );
Following is my trigger, which is incorrect ofcourse
create or replace
trigger compute_func
after insert on sampletable
for each row
declare
returned_value raw(30);
begin
returned_value := my_func(sampleable.xmlcolumn);
update sampletrigger set checkfield = returned_value;
end;
Don't use update just assign the value. Additionally you need to use a BEFORE trigger, because you cannot change column values in an AFTER trigger.
create or replace trigger compute_func
before insert on sampletable
for each row
begin
:new.checkfield := my_func(:new.xmlcolumn);
end;
More details in the manual: http://docs.oracle.com/cd/E11882_01/appdev.112/e25519/triggers.htm#LNPLS655