Trigger function not working in postgresql - sql

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

Related

Trigger insert of data into table with result from another select query

Starting my first steps in SQL.
I'm trying to insert the last row of a timestamped table to another table.
I've written this trigger:
CREATE TRIGGER update_analysis()
AFTER INSERT ON data
FOR EACH ROW
EXECUTE PROCEDURE insert_to_analysis();
I've defined a function, which I know is wrong, but don't understand how to write it correctly. (the target table has these columns)
CREATE FUNCTION UPDATE_ANALYSIS() RETURNS TABLE
AS
$$ BEGIN
INSERT INTO ANALYSIS (TIME, CYCLE_NUMBER,CAT1,CAT2,CAT3)
SELECT (TIME, CYCLENO , I1 , I2 * 2 ,I3*3)
FROM DATA
ORDER BY TIME DESC
LIMIT 1;)
RETURN
END;
$$ LANGUAGE 'plpgsql';
Thanks in advance
If you are referencing the same data you just inserted into data, you could simply refer to what you inserted instead of SELECT:ing, like so:
CREATE FUNCTION UPDATE_ANALYSIS() RETURNS TABLE
AS
$$ BEGIN
INSERT INTO ANALYSIS (TIME, CYCLE_NUMBER,CAT1,CAT2,CAT3)
VALUES (NEW.TIME, NEW.CYCLENO , NEW.I1 , NEW.I2 * 2 ,NEW.I3 * 3);
RETURN NEW;
END;
$$ LANGUAGE 'plpgsql';
Since it's a function for a trigger, it should return a trigger.
And it's for each row, so an insert from values is possible.
Example
CREATE FUNCTION FN_INSERT_TO_ANALYSIS() RETURNS TRIGGER
AS $ins_analysis$
BEGIN
INSERT INTO ANALYSIS (TIME, CYCLE_NUMBER, CAT1, CAT2, CAT3)
VALUES (NEW.TIME, NEW.CYCLENO, NEW.I1, NEW.I2 * 2 , NEW.I3 * 3);
RETURN NEW;
END $ins_analysis$ LANGUAGE 'plpgsql';
CREATE TRIGGER trg_data_ins_analysis
AFTER INSERT ON data
FOR EACH ROW
EXECUTE PROCEDURE fn_insert_to_analysis();
insert into data values (current_timestamp,1,1,1,1)
select * from data
time
cycleno
i1
i2
i3
2021-12-27 14:53:17.822649
1
1
1
1
select * from ANALYSIS
time
cycle_number
cat1
cat2
cat3
2021-12-27 14:53:17.822649
1
1
2
3
Demo on db<>fiddle here

How to use a newly inserted value from another table in PostgreSQL?

Assuming I have two tables final table and table_1, I want to use the the newest values from table_1 and insert them with a trigger in the final_table with every INSERT ON table_1. When I create the triggerfunction inserttrigger() as shown in the example and create the trigger, I get the newest value times the number of rows in table_1. How to write the trigger proper that I get only the single newest record in table1?
Doing:
-- Create tables and inserting example values
CREATE TABLE final_table(id INTEGER, value_fin INTEGER);
CREATE TABLE table_1(id INTEGER, value INTEGER);
INSERT INTO table_1 VALUES(1, 200), (2,203), (3, 209);
-- Create Triggerfunction
CREATE OR REPLACE FUNCTION inserttrigger()
RETURNS TRIGGER AS
$func$
BEGIN
INSERT INTO final_table
SELECT latest.id, latest.value
FROM (SELECT NEW.id, NEW.value FROM table_1) AS latest;
RETURN NEW;
END;
$func$ language plpgsql;
-- Create Trigger
CREATE TRIGGER final_table_update BEFORE INSERT ON table_1
FOR EACH ROW EXECUTE PROCEDURE inserttrigger() ;
--Insert example values
INSERT INTO table_1 VALUES(4, 215);
Results in:
SELECT * FROM final_table
id | value_fin
4 215
4 215
4 215
But should look like:
id | value_fin
4 215
While:
CREATE TRIGGER final_table_update BEFORE INSERT ON table_1
EXECUTE PROCEDURE inserttrigger() ;
Results in:
ERROR: record "new" is not assigned yet
DETAIL: The tuple structure of a not-yet-assigned record is indeterminate.
I would recommend the VALUES() syntax:
CREATE OR REPLACE FUNCTION inserttrigger()
RETURNS TRIGGER AS
$func$
BEGIN
INSERT INTO final_table VALUES(NEW.id, NEW.value);
RETURN NEW;
END;
$func$ language plpgsql;
CREATE TRIGGER final_table_update BEFORE INSERT ON table_1
FOR EACH ROW EXECUTE PROCEDURE inserttrigger();
Note that you could also get the same behavior with a common-table-expression and the returning syntax, which avoids the need for a trigger:
with t1 as (
insert into table_1(id, value_fin) values(4, 215)
returning id, value_fin
)
insert into final_table(id, value) select id, value_fin from t1

Change number of Rows Affected by Update

I am trying to achieve here is to basically override 0 rows Updated, when UPDATE is issued in-case the actual PK/UK value doesn't exist in the table. This is what I have done:
Actual Table:
CREATE TABLE fdrgiit.vereine(
team numeric(10) primary key,
punkte int not null,
serie int not null
);
Dummy Table:
CREATE TABLE fdrgiit.dummyup
(
id numeric(1) PRIMARY KEY,
datetest timestamp
);
Inserted records in both the tables:
insert into vereine(team,punkte,serie) values(1, 50, 1);
insert into vereine(team,punkte,serie) values(2, 30, 1);
insert into vereine(team,punkte,serie) values(3, 25, 1);
insert into vereine(team,punkte,serie) values(4, 37, 2);
insert into dummyup values(1, now());
Created the following function and trigger:
create or replace function updateover()
returns trigger as
$BODY$
begin
if EXISTS (select 1 FROM vereine WHERE team = new.team ) then
RETURN NEW;
else
UPDATE fdrgiit.dummyup set datetest=now() where id=1;
RETURN NULL;
end if;
end;
$BODY$
LANGUAGE plpgsql;
create trigger update_redundancy
before update on vereine
for each row
execute procedure updateover() ;
But when I execute an UPDATE like this on the , I am still get 0 rows affected
update vereine set punkte=87 where team=5;
Kindly review and please suggest if this is something that can be done.
You cannot trigger anything with an UPDATE that does not affect row as triggers are only fired for affected rows.
But you could wrap your alternative UPDATE into a function:
CREATE OR REPLACE FUNCTION updateover()
RETURNS int AS
$func$
UPDATE dummyup
SET datetest = now()
WHERE id = 1
RETURNING 2;
$func$ LANGUAGE sql;
... and run your UPDATE nested like this:
WITH upd AS (
UPDATE vereine
SET punkte = 87
WHERE team = 5 -- does not exist!
RETURNING 1
)
SELECT 1 FROM upd
UNION ALL
SELECT updateover()
LIMIT 1;
db<>fiddle here
If no row qualifies for an UPDATE, then 1st outer SELECT 1 FROM upd returns no row and Postgres keeps processing the 2nd SELECT updateover(). But if at least one row is affected, the final SELECT is never executed. Exactly what you want.
This updates dummyup one time if the UPDATE on vereine does not affect any rows; never several times. But that's ok, since now() is STABLE for the duration of the transaction.
Related:
Return a value if no record is found

PostgreSQL - How to keep a column updated

I'm new to SQL and I'm trying to update a column (ex_counter) of a table (ex_table). This column consists of a counter of the number of times an ID (ex_id) appears on a second table (ex2_id in ex2_table).
An ID can be inserted into the second table at any moment. If that ID is already existing, the counter of its corresponding ID in the first table must be updated (by simply adding 1, I guess).
These are the two tables:
CREATE TABLE ex_table(
ex_id SMALLINT,
ex_counter SMALLINT;)
CREATE TABLE ex2_table(
ex2_id SMALLINT;)
I think it should be done more or less like this. The commented code is the pseudocode that I don't know how to implement:
CREATE TRIGGER ex_trigger AFTER
INSERT ON ex2_table
FOR EACH ROW EXECUTE PROCEDURE ex_func();
CREATE FUNCTION ex_func() RETURNS trigger AS $$ BEGIN
/*
if ex2_id = ex_id
ex_counter = ex_counter + 1
*/
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
Thanks in advance!
Something like this
IF EXISTS (SELECT * FROM ex_table WHERE ex_id = new.ex2_id) THEN
UPDATE ex_table
SET ex_counter = ex_counter + 1
WHERE ex_id = new.ex2_id
ELSE
INSERT INTO ex_table VALUES (new.ex2_id, 1)
END IF;
Note that it is no point really to store a counter since you so easily can retrieve the value by doing a SELECT COUNT()...

table is mutating trigger cannot see it

I have a table like so...
create table grades (id number, t1 number, t2 number, t3 number, mark number);
I want a trigger to calculate and update the table with the average mark for each row affected.
so I write a trigger like so...
create or replace trigger rades_trg
after insert
on grades
for each row
declare
v_mark grades.mark%type;
begin
v_mark := (:new.t1 + :new.t2 + :new.t3) / 3;
update grades set mark = v_mark where id = :new.id;
dbms_output.put_line(v_mark);
end;
/
then I enter a new record like so...
insert into grades (id, t1, t2, t3) values (5, 100, 100, 25);
but oracle tells me the table may be mutating and the trigger cannot see it.
Row triggers are generally not allowed to query or modify the table on which they're declared. To work around this I suggest that you make your trigger a BEFORE trigger so you can simply set :new.mark in your trigger:
create or replace trigger rades_trg
before insert on grades
for each row
begin
:new.mark := (:new.t1 + :new.t2 + :new.t3) / 3;
dbms_output.put_line(:new.mark);
end;
Best of luck.