How to update a table with a trigger - sql

I am trying to create a trigger that reads from table 2 and updates a column in table 1.
I tried with this method but an exception occurred: ORA-04091:table table1 is mutating .
CREATE OR REPLACE TRIGGER "TRG1"
AFTER INSERT OR UPDATE ON table1
FOR EACH ROW
BEGIN
UPDATE table1 SET name =(SELECT name FROM table2
WHERE table1.id = table2.id);
END;

Use a BEFORE UPDATE trigger (as you cannot modify values after they have been inserted or updated) and use the :NEW record (rather that trying to update the table and getting into an infinite loop of triggers):
CREATE OR REPLACE TRIGGER TRG1
BEFORE INSERT OR UPDATE ON table1
FOR EACH ROW
BEGIN
SELECT name
INTO :NEW.name
FROM table2
WHERE :NEW.id = id;
EXCEPTION
WHEN NO_DATA_FOUND THEN
:NEW.name := NULL;
END;
/
fiddle

You can only update the name in a before trigger
CREATE OR REPLACE TRIGGER "TRG1"
BEFORE INSERT OR UPDATE ON table1
FOR EACH ROW
DECLARE new_name varchar2(100);
BEGIN
SELECT name INTO new_name FROM table2
WHERE :new.id = table2.id;
:new.name :=new_name;
END;
/
MTO was so nice to make a fiddle

Related

Trigger, which logs/deletes a row into a separate table, before an entry with the same attribute value is inserted

I have two tables which both have the same columns and a foreign key named id. Table t1 is supposed to store the current data while table t2 is a log table.
The rows in table t1 are supposed to be unique, so I want to create a trigger which copies the data from t1 to t2 and then removes the entries in t1 (if I insert data with an id which is already found in t1).
If the id is not found in t1, the trigger is simply supposed to insert the data into t1 and do nothing with t2. I am using PostgreSQL and this is my current attempt but it doesn't seem to work:
CREATE OR REPLACE FUNCTION move_data_to_historical_measured_data_insert()
RETURNS TRIGGER
LANGUAGE PLPGSQL
AS
$$
BEGIN
IF OLD.id = NEW.id THEN
INSERT INTO t2(id, value, timestamp)
VALUES(OLD.id, OLD.value, OLD.timestamp);
DELETE FROM t1;
RETURN NULL;
ELSE
RETURN NULL;
END IF;
END;
$$
CREATE OR REPLACE TRIGGER store_data_to_history_insert
BEFORE INSERT
ON t1
FOR EACH ROW
EXECUTE PROCEDURE move_data_to_historical_measured_data_insert();
CREATE OR REPLACE FUNCTION move_data_to_historical_measured_data_insert()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
begin
if (exists(select t1.id from t1 where t1.id = new.id)) then
insert into t2 (id, value, timestamp)
select t1.id, t1.value, t1.timestamp from t1 where t1.id = new.id;
delete from t1 where t1.id = new.id;
end if;
return new;
END;
$function$
;
CREATE OR REPLACE TRIGGER store_data_to_history_insert
BEFORE INSERT
ON t1
FOR EACH ROW
EXECUTE PROCEDURE move_data_to_historical_measured_data_insert();

How to create trigger when one rows is updated to update another table

I have two tables, that has references with ID of products. Tables are like below.
Table 1 : ID, Rgrupa
Table 2: ID, Rgrupa
I would like to create trigger, that will update Rgrupa from table 2, when Rgrupa is updated in table 1, for the same ID of product.
I have tried this
create or replace trigger test
after update of rgrupa on table1
begin
update table2 t2
set t2.rgrupa = :new.rgrupa
where t2.id = :new.id;
end;
And ERROR MESSAGE is:
Error: ORA-04082: NEW or OLD references not allowed in table level triggers
If I run your code:
SQL> CREATE OR REPLACE TRIGGER test
2 AFTER UPDATE OF rgrupa
3 ON table1
4 BEGIN
5 UPDATE table2 t2
6 SET t2.rgrupa = :new.rgrupa
7 WHERE t2.id = :new.id;
8 END;
9 /
CREATE OR REPLACE TRIGGER test
*
ERROR at line 1:
ORA-04082: NEW or OLD references not allowed in table level triggers
Oracle clearly says that you have an issue in your code.
If you want to use the :new, you need a row-level trigger:
SQL> CREATE OR REPLACE TRIGGER test
2 AFTER UPDATE OF rgrupa
3 ON table1
4 FOR EACH ROW
5 BEGIN
6 UPDATE table2 t2
7 SET t2.rgrupa = :new.rgrupa
8 WHERE t2.id = :new.id;
9 END;
10 /
Trigger created.
I used below structure to create the trigger
create table table1 (id int, rgrupa varchar2(100));
create table table2 (id int, rgrupa varchar2(100));
insert into table1 (id,rgrupa) values (1,'A');
insert into table1 (id,rgrupa) values (2,'B');
insert into table1 (id,rgrupa) values (3,'C');
insert into table2 (id,rgrupa) values (1,'A');
insert into table2 (id,rgrupa) values (2,'B');
commit;
select * from table1;
select * from table2;
create or replace trigger sandeeptest after update of rgrupa on table1 for each row
begin
update table2 set rgrupa=:new.rgrupa where id=:new.id;
end;
update table1 set rgrupa='Aa' where rgrupa='A';
update table1 set rgrupa='Cc' where rgrupa='C';

How to create function that updates changes in one table from another table?

I have this function that I found here:
Insert trigger to Update another table using PostgreSQL
CREATE TABLE table1
(
id integer NOT NULL,
name character varying,
CONSTRAINT table1_pkey PRIMARY KEY (id)
)
CREATE TABLE table2
(
id integer NOT NULL,
name character varying
)
CREATE OR REPLACE FUNCTION function_copy() RETURNS TRIGGER AS
$BODY$
BEGIN
INSERT INTO
table2(id,name)
VALUES(new.id,new.name);
RETURN new;
END;
$BODY$
language plpgsql;
CREATE TRIGGER trig_copy
AFTER INSERT ON table1
FOR EACH ROW
EXECUTE PROCEDURE function_copy();
If I insert these two rows:
insert into table1 values (1, 'Andrey');
insert into table1 values (2, 'Mariza');
Then they also go into table2.
My problem is when I do an update on a value:
update table1 set name = 'Andi' where id = '1';
nothing happens in table2.
How can I create a function that updates changes in table?
To support UPDATE you can do this:
Trigger:
CREATE TRIGGER trig_copy
AFTER INSERT OR UPDATE ON table1
FOR EACH ROW
EXECUTE PROCEDURE function_copy();
Function:
CREATE OR REPLACE FUNCTION function_copy() RETURNS TRIGGER AS
$BODY$
BEGIN
if TG_OP='INSERT' then
INSERT INTO table2(id,name) VALUES(new.id,new.name);
end if;
if TG_OP='UPDATE' then
Update table2 set name=new.name where id=old.id;
end if;
RETURN new;
END;
$BODY$
language plpgsql;
create or replace trigger trig_copy
after insert or update
on table1
begin
merge into table2 t2
using (
select
id,
name
from
table1 t1) t_new
on (t2.id = t_new.id)
when matched then
update
set
t2.name = t_new.name
where
t2.id = t_new.id
WHEN NOT MATCHED then
insert (id,name) values(t_new.id,t_new.name);
end;
This will be applied to all records, not only updated record.
If U want to update/insert only one record Create trigger
referencing new as new
for each row
and use :new.id :new.name , to find record You want in t2.
Thanks to all for help. This is the answer that had worked for me.
I took it from Elads and Justas solutions:
CREATE TRIGGER trig_copy
AFTER INSERT OR UPDATE ON table1
FOR EACH ROW
EXECUTE PROCEDURE function_copy();
CREATE OR REPLACE FUNCTION function_copy() RETURNS TRIGGER AS
$BODY$
BEGIN
if TG_OP='INSERT' then
INSERT INTO table2
(SELECT * FROM table1
WHERE id NOT IN
(SELECT id FROM table2));
end if;
if TG_OP='UPDATE' then
Update table2 set name=new.name where id=old.id;
end if;
RETURN new;
end;
$BODY$
language plpgsql;

After insert Trigger

I'm trying to do a trigger which insert some into column after insert statement. For example I have table with column which looks:
Column1 Column2 Column3
And I'm inserting data into Column 1, Insert into Table(Column1) values ('256234','234234').
Now I would like automatically insert into COlumn2 TImestamp and into Column3 Value "Y", So output should looks:
Column1 Column2 Column3
256234 2015-10-28 08:48 Y
234234 2015-10-28 08:48 Y
Guys, could you help me with that? I tried to use cursor
Finally I got something like that:
create or replace trigger name
after insert on table
declare
c1 sys_refcursor;
idx varchar2(200);
begin
open c1 for select Column1 from table ;
loop
fetch c1 into idx;
exit when c1%NOTFOUND;
update table a1 set a1.Column2 = (select to_char(sysdate,'YYYYMMDDHHMISS') from dual) where Column1=idx;
update table a1 set a1.Column3 = (select 'Y' from dual) where Column1=idx;
end loop;
close c1;
end;
It works fine, but I'm wondering if there is some other better solution than that?
No need for a cursor or even an update:
create or replace trigger name
before insert on table_x
begin
:new.column2 := sysdate;
:new.column3 := 'Y';
end;
/
But you need a before trigger for this, because an after trigger cannot modify the newly inserted row.
But why don't you just define a default value for those columns, then you don't need a trigger at all:
create table table_x
(
column_1 integer,
column_2 date default sysdate,
column_3 varchar(1) default 'Y'
);
You can create trigger as similar :
CREATE OR REPLACE TRIGGER "TRG_NAME"
BEFORE INSERT ON "TABLE_NAME"
FOR EACH ROW
DECLARE
BEGIN
:NEW.Column2 := to_char(sysdate,'YYYYMMDDHHMISS');
:NEW.Column3 := 'Y';
END TRG_NAME ;
/
ALTER TRIGGER "TRG_NAME" ENABLE;
/
Hope this PL/SQL will help you..

sql trigger using two tables data

So I have three tables. First two have ID column and "rank" column (an integer). I will be inserting into the third table both IDs (ID1, ID2) but I need the trigger to check whether they are the same rank before I can insert. I can't get it to work.
CREATE OR REPLACE TRIGGER TRIGGER1
AFTER INSERT ON TABLE_C
BEGIN
IF NOT EXISTS (
SELECT TABLE_A.id, TABLE_B.id
FROM TABLE_A JOIN TABLE_B ON TABLE_A.rank = TABLE_B.rank
WHERE TABLE_A.id = inserted.id1 AND TABLE_B.id = inserted.id2 )
THEN
PRINT 'Not the same rank'
ROLLBACK
END
END;
I'm using Oracle db.
you can not use 'Rollback' or 'commit' in Oracle Triggers -
please refer this
I think you can modify your trigger as follow -
CREATE OR REPLACE TRIGGER Trigger1
AFTER INSERT ON Table_c
FOR EACH ROW
DECLARE
l_Count NUMBER := 0;
BEGIN
SELECT COUNT(*)
INTO l_Count
FROM Table_a
JOIN Table_b ON Table_a.Rank = Table_b.Rank
WHERE Table_a.Id = :NEW.Id1
AND Table_b.Id = :NEW.Id2;
IF l_Count = 0 THEN
DELETE FROM Table_c
WHERE Id1 = :NEW.Id1
AND Id2 = :NEW.Id2;
--PRINT 'Not the same rank'
Dbms_Output.Put_Line('Not the same rank');
END IF;
END;
/
Triggers are two kinds, BEFORE and AFTER Triggers. If you want to check whether the data has the same rank before the data is inserted, then you should use a BEFORE Trigger.
CREATE OR REPLACE TRIGGER TRIGGER1
BEFORE INSERT ON TABLE_C
Then the logic will follow.
UPDATE: You can easily raise application error. Such as:
IF HasSameRank == 0 THEN
raise_application_error(-21013, 'Not the same rank');
END IF;