SQL trigger update another table after insert - sql

I would like to make a trigger that changes the value in one table, after updating the other table. Let's say I have such 2 tables
first table
A(
id serial,
value1 int,
A_date date);
second table
B(
id serial,
value2 int,
B_date date);
Let's say that after inserting a value 50 to table A field value1, I want to increase the value2 by 50 in table B. I did so, I get no errors, but the result in table B does not change for me.Why?
create function Fun()
returns trigger
language plpgsql
as $$
begin
if(TG_OP='INSERT') then
update B
set value2= value2+new.value1
where "id"=1;
end if;
return null;
end ;
$$;
create trigger Fun_trig
after insert
on A
for each row
execute procedure Fun()

Related

Trigger function not working in postgresql

I have tables as follows:
test
address
value
a1
50
table1
address
amount
id
hash
a1
50
2
se2
I am trying to add the amount to a1 when a new insertion of a1 is added to the test table i.e. if a new row is added to test i.e. address = a1 and value = 100 then, i want to update the amount in a1 on table1 table i.e. amount = 50 + 100 = 150 but the value does not get updated.
Here is my trigger implementation:
CREATE OR REPLACE FUNCTION address_trigger()
RETURNS trigger LANGUAGE plpgsql AS $$
BEGIN
UPDATE table1
SET amount = amount + new.value
WHERE NEW.address = table1.address;
RETURN NEW;
end;
$$;
CREATE TRIGGER update_table1
AFTER INSERT
ON "test"
FOR EACH ROW
EXECUTE PROCEDURE address_trigger();
I added the trigger function via query console and the trigger function shows on the database as update_table1 in intellij. then, i insert a new row to test manually INSERT INTO test(address, value) VALUES ('a1',100);. the row gets updated on test but the value is not updated on table1 on reloading. Could someone help me out, what am i doing wrong here?
It works ok for me. Create tables
create table "test"(
address varchar(10),
value int
);
create table table1 (
address varchar(10),
amount int
);
insert into table1
values('abc', 0);
Your trigger
CREATE OR REPLACE FUNCTION address_trigger()
RETURNS trigger LANGUAGE plpgsql AS $$
BEGIN
UPDATE table1
SET amount = amount + new.value
WHERE NEW.address = table1.address;
RETURN NEW;
end;
$$;
CREATE TRIGGER update_table1
AFTER INSERT
ON "test"
FOR EACH ROW
EXECUTE PROCEDURE address_trigger();
Test it
insert into "test"
values ('abc', 10),
('abc', 30);
select *
from table1;
Output
address amount
abc 40

Updating another table on conflict postgres

I have a requirement to insert on one table (test01) and update on another table(result) whenever the conflict arises. I tried with below query:
insert into test01 as cst (col1,col2)
select col1,col2 from (
select 1 col1,'test' col2) as excluded
on conflict (col1) do
update result as rst set conflictid = excluded.col1, updated_at = now() where rst.conflictid= excluded.col1 ;
but its returns "syntax error at or near result". Can anyone please help me with the right solution.
Basically, your approach is not feasible. The ON CONFLICT ... DO UPDATE clause applies only to the table into which the rows are inserted. See INSERT syntax in the documentation.
A solution requires a bit more work. You should create a trigger for table test01 to get the effect you want.
Example tables (slightly changed names of columns and table):
create table test01_conflicts(
conflict_id int primary key,
updated_at timestamp);
create table test01(
id int primary key,
str text);
When the table test01 is updated with the same id the trigger function inserts or updates a row in the conflict table. Note that in this case the function returns null so the update on test01 will not proceed.
create or replace function before_update_on_test01()
returns trigger language plpgsql as $$
begin
if new.id = old.id then
insert into test01_conflicts(conflict_id, updated_at)
values (new.id, now())
on conflict(conflict_id)
do update set updated_at = now();
return null;
end if;
return new;
end $$;
create trigger before_update_on_test01
before update on test01
for each row execute procedure before_update_on_test01();
The query. On conflict update test01 with the same id only:
insert into test01
select col1, col2
from (
select 1 as col1, 'test' as col2
) as source
on conflict (id) do
update set
id = excluded.id;

trigger insert only if tupel satisfies condition

I want to create a trigger which checks before insert if the tupel which is supposed to be inserted holds a specific condition (which also depends on another table).
For example:
create trigger or replace check_tupel
before insert on A
for each row
execute
if exists (select x,y from B where B.x = A.x and B.y = A.y)
Oh I am using postgreSQL 13.
EDIT: Yes I know that I can do this without a trigger, but I am asking for a solution with a trigger for a reason.
I hope there is a way to do this... My other idea was to create a UDF which gets called before insert but I do not know how to check the condition in this UDF and only insert if the function returns true.
If you simply wanna automatically validate a record before inserting it on table A based on table B using a User Defined Function, you do not need a trigger at all. Consider adding a simple CHECK CONSTRAINT:
CREATE TABLE a (
x int,
y int,
CONSTRAINT exists_in_b CHECK (NOT myfunc(x,y))
);
Demo: db<>fiddle
CREATE TABLE b (x int,
y int);
INSERT INTO b VALUES (42,42);
CREATE OR REPLACE FUNCTION myfunc(x int, y int)
RETURNS BOOLEAN AS $BODY$
SELECT EXISTS (SELECT 1 FROM b WHERE b.y =$1 AND b.x=$2 )
$BODY$
LANGUAGE sql;
CREATE TABLE a (
x int,
y int,
CONSTRAINT exists_in_b CHECK (NOT myfunc(x,y)) -- here the magic happens
);
Now, if we try to insert a value that our function does not validate, it raises an exception:
INSERT INTO a VALUES (42,42);
ERROR: new row for relation "a" violates check constraint "exists_in_b"
DETAIL: Failing row contains (42, 42).
SQL state: 23514
EDIT (See comments): Solution using a trigger
CREATE OR REPLACE FUNCTION myfunc()
RETURNS TRIGGER AS $BODY$
BEGIN
IF EXISTS (SELECT 1 FROM b WHERE b.y =NEW.y AND b.x=NEW.x ) THEN
RAISE EXCEPTION 'tuple already exists in "b": % %', NEW.x,NEW.y;
END IF;
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql;
CREATE TRIGGER check_tupel
BEFORE INSERT OR UPDATE ON a
FOR EACH ROW EXECUTE PROCEDURE myfunc();
Demo: db<>fiddle
So you need a trigger solution, sounds like a homework problem. The question then becomes do you want to:
abort the entire operation
slightly squash the row but continue the remainder of the operation.
The following does the second: (See demo)
create or replace
function check_b_has_a()
returns trigger
language plpgsql
as $$
begin
if exists
( select null
from b
where (b.x,b.y) =
(new.x, new.y)
)
then
return null;
else
return new;
end if;
end;
$$;
create trigger a_bir
before insert
on a
for each row
execute function check_b_has_a();

PostgreSQL - Shared temp table between functions

I wnat to know if its possible to share a temporary table between functions that are called in a "main function", like this:
-- some sub function
create or replace function up_sub_function (str text)
returns table (id int, descr text) as $$
begin
return query select * from temp_table where descr like concat('%', str , '%');
end; $$
language plpgsql;
-- main function
create or replace function up_main_function ()
returns table (id int, descr text) as $$
begin
create temporary table temp_table if not exists (
id int,
descr text
);
insert into temp_campaigns select id, descr from test_table;
return query select * from up_sub_function('a');
end; $$
language plpgsql;
BEGIN;
select * from up_main_function();
drop table temp_table;
COMMIT;
If you can show me the correct way to achieve this, I want to be able to populate a temporary table and then filter rows by calling othe functions inside the main function.
Thanks ans happy programming! :)
See the documentation https://www.postgresql.org/docs/current/static/sql-createtable.html
temp tables are valid for the entire session. That is as long as you stay connected to the database.
In your case you only need it during the transaction. So you should create it with ON COMMIT DROP
create temporary table temp_table if not exists (
id int,
descr text
) ON COMMIT DROP;
Once you created the table you can use it within any function in the current transaction.
You do not need the BEGIN to start the transaction. A transaction is automatically started when the outer function is called.
Nested function calls share the same transaction. So they all see the table.

in postgres, splitting an update between two tables using Rules

attempting to maintain an edit log using rules.
create table t1(
id serial primary key,
c1 text,
... );
create table edit_log(
id int references t1,
editor_id int references users,
edit_ts timestamp default current_timestamp );
with an update, wish to update t1 and insert into edit_lot
update t1 set c1='abc', ... where id=456;
insert into edit_log( id, editor_id, current_timestamp );
this would be a pretty straightforward except for the arbitrary number of columns, eg,
update t1 set c1='abc', c2='def', editor_id=123 where id=456;
update t1 set c3='xyz', editor_id=123 where id=456;
how to write a rule for that?
I think a trigger will serve you better than a rule. Consider this demo.
Test setup
CREATE TEMP TABLE t1(id int, editor_id int, c1 text);
INSERT INTO t1(id, editor_id) VALUES (1,1),(2,2);
CREATE TEMP TABLE edit_log(id int, editor_id int, edit_ts timestamp);
Create trigger function
CREATE OR REPLACE FUNCTION trg_t1_upaft_log()
RETURNS trigger AS
$BODY$
BEGIN
IF OLD IS DISTINCT FROM NEW THEN -- to avoid empty updates
INSERT INTO edit_log(id, editor_id, edit_ts)
VALUES(NEW.id, NEW.editor_id, now()::timestamp);
END IF;
RETURN NULL; -- trigger will be fired AFTER updates, return value is irrelevant.
END;
$BODY$
LANGUAGE plpgsql VOLATILE;
Create trigger
CREATE TRIGGER upaft_log
AFTER UPDATE ON t1
FOR EACH ROW
EXECUTE PROCEDURE trg_t1_upaft_log();
Test
UPDATE t1 SET c1 = 'baz' WHERE id = 1;
SELECT * FROM edit_log; -- 1 new entry
UPDATE t1 SET c1 = 'baz' WHERE id = 1;
SELECT * FROM edit_log; -- no new entry, update changed nothing!
UPDATE t1 SET c1 = 'blarg';
SELECT * FROM edit_log; -- 2 new entries, update changed two rows.
Cleanup
DROP TRIGGER upaft_log ON t1;
DROP FUNCTION trg_t1_upaft_log()
-- Temp. tables will be dropped automatically at end of session.
Comment
It is very hard or plain impossible (depending on the details of your setup) for a rule to figure out which rows are updated.
A trigger AFTER UPDATE can decide after the fact and is the better choice. Also easy to integrate with (most) additional triggers and / or rules in this scenario.