Postgres update trigger - postgresql

I've problem with a trigger function in postgresql.
Here my simple code.
CREATE TABLE specie
(specie_id INT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
nome_comune TEXT UNIQUE,
nome_scientifico TEXT UNIQUE);
CREATE TABLE rilevatore
(rilevatore_id INT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
nome_cognome TEXT);
CREATE TABLE evento_investimento
(evento_id INT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
data DATE,
ora TIME WITHOUT TIME ZONE,
rilevatore_id INT REFERENCES rilevatore (rilevatore_id),
specie_id INT REFERENCES specie(specie_id));
CREATE VIEW inserimento_dati_vista AS
SELECT row_number() OVER ()::integer AS gid,
evento_investimento.ora,
evento_investimento.data,
rilevatore.nome_cognome,
specie.nome_comune,
specie.nome_scientifico
FROM evento_investimento
JOIN specie ON evento_investimento.specie_id = specie.specie_id
JOIN rilevatore ON evento_investimento.rilevatore_id = rilevatore.rilevatore_id;
CREATE OR REPLACE FUNCTION inserimento_dati_fun_2() RETURNS trigger AS $$
BEGIN
if not exists(select * from rilevatore where rilevatore.nome_cognome=new.nome_cognome) then
INSERT INTO rilevatore (nome_cognome)
VALUES (NEW.nome_cognome);
end if;
if not exists(select * from specie where specie.nome_comune=new.nome_comune) then
INSERT INTO specie (nome_comune, nome_scientifico)
VALUES (NEW.nome_comune, NEW.nome_scientifico);
end if;
INSERT INTO evento_investimento (data, ora, rilevatore_id, specie_id)
VALUES (NEW.data,NEW.ora,
(SELECT rilevatore_id FROM rilevatore WHERE rilevatore.nome_cognome = NEW.nome_cognome),
(SELECT specie_id FROM specie WHERE specie.nome_comune = NEW.nome_comune));
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
create trigger inserimento_dati_fun_trg
instead of insert on inserimento_dati_vista for each row EXECUTE procedure inserimento_dati_fun_2();
Now, I want to add a function that allow to update all the tables by using the view inserimento_dati_vista.
I've tried with a simple code to update only the data column
CREATE OR REPLACE FUNCTION update_dati_fun_2() RETURNS TRIGGER AS $$
BEGIN
IF (TG_OP = 'UPDATE') THEN
IF old.data is distinct from new.data then
UPDATE evento_investimento
SET data = new.data;
END IF;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
create trigger update_dati_fun_2_trg
instead of update on inserimento_dati_vista for each row EXECUTE procedure update_dati_fun_2();
However when I perfomr the query in order to update only a row, the trigger update all the rows in the table. Here some code to fill data.
INSERT INTO inserimento_dati_vista
(data, ora, nome_cognome, nome_comune, nome_scientifico)
VALUES
('2020-01-01', '16:54:00','mario', 'lupo', 'Canis lupus'),
('2020-01-02', '13:54:00','luca', 'lontra', 'Lutra lutra');
UPDATE inserimento_dati_vista
SET data = '2021-01-02' where nome_cognome = 'luca'

Update function is:
CREATE OR REPLACE FUNCTION update_dati_fun_2() RETURNS TRIGGER AS $$
BEGIN
IF (TG_OP = 'UPDATE') THEN
IF old.data is distinct from new.data then
UPDATE evento_investimento e
SET data = new.data
FROM rilevatore r
WHERE nome_cognome = new.nome_cognome AND r.rilevatore_id = e.rilevatore_id;
END IF;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;

Related

How to set a sum of column to variable in trigger function?

I am writing a trigger for a table in PostgreSQL 14 with which I could get the amounts I need, add them up and write them from a row. But I can't write data to variables after SELECT in any way. Here is my code:
CREATE TABLE folder(
Id uuid unique,
Url varchar(255),
Date timestamp,
ParentId uuid,
Size int
);
CREATE TABLE file(
Id uuid unique,
Url varchar(255),
Date timestamp,
ParentId uuid,
Size int,
FOREIGN KEY (ParentId) REFERENCES Folder (Id) ON DELETE CASCADE
);
CREATE OR REPLACE FUNCTION update_dates_and_price_update_folder() RETURNS trigger AS $update_dates_and_price_update_folder$
DECLARE
folders_sum integer;
files_sum integer;
BEGIN
SELECT (sum(size)) FROM file WHERE parentId = NEW.id INTO folders_sum;
SELECT (sum(size)) FROM folder WHERE parentId = NEW.id INTO files_sum;
NEW.size = folders_sum + files_sum;
UPDATE folder set date = NEW.date WHERE id = NEW.parentId OR id = OLD.parentId;
RETURN NEW;
END;
$update_dates_and_price_update_folder$ LANGUAGE plpgsql;
CREATE OR REPLACE TRIGGER update_data_folder AFTER UPDATE
ON folder
FOR ROW
EXECUTE FUNCTION update_dates_and_price_update_folder();
I checked the values of variables via RAISE NOTICE, both variables are always null. If you make such a request:
SELECT (sum(size)) FROM file WHERE parentId = some_id;
It's not null.
select response
Solution
CREATE OR REPLACE FUNCTION update_price_folder() RETURNS trigger AS $update_price_update_folder$
DECLARE
folders_sum integer;
files_sum integer;
file_item file;
folder_item folder;
BEGIN
files_sum := 0;
FOR file_item IN SELECT * FROM file WHERE parentId = NEW.id
LOOP
IF file_item.size IS NOT NULL THEN
files_sum := files_sum + file_item.size;
END IF;
END LOOP;
folders_sum := 0;
FOR folder_item IN SELECT * FROM folder WHERE parentId = NEW.id
LOOP
IF folder_item.size IS NOT NULL THEN
folders_sum := folders_sum + folder_item.size;
END IF;
END LOOP;
NEW.size = folders_sum + files_sum;
RETURN NEW;
END;
$update_price_update_folder$ LANGUAGE plpgsql;
CREATE OR REPLACE TRIGGER update_price_folder_trigger BEFORE UPDATE
ON folder
FOR ROW
EXECUTE FUNCTION update_price_folder();
CREATE OR REPLACE FUNCTION update_dates_folder() RETURNS trigger AS $update_dates_folder$
BEGIN
UPDATE folder set date = NEW.date WHERE id = NEW.parentId OR id = OLD.parentId;
RETURN NEW;
END;
$update_dates_folder$ LANGUAGE plpgsql;
CREATE OR REPLACE TRIGGER update_date_folder AFTER UPDATE
ON folder
FOR ROW
EXECUTE FUNCTION update_dates_folder();

PostgreSQL - Trigger on INSERT or UPDATE

I wan't to add a trigger in a PostgreSQL database. This trigger is used to concatenate values of 2 columns to update a 3rd one. I wan't to run it when a row is inserted or updated in the table.
Table
CREATE TABLE IF NOT EXISTS FILE(ID INT NOT NULL PRIMARY KEY, FOLDER TEXT, NAME TEXT, URL TEXT);
Function
CREATE OR REPLACE FUNCTION LINK() RETURNS trigger AS
$$
BEGIN
IF (TG_OP = 'UPDATE') THEN
UPDATE FILE
SET URL = CONCAT(FOLDER, NAME)
WHERE ID = OLD.ID;
ELSIF (TG_OP = 'INSERT') THEN
UPDATE FILE
SET URL = CONCAT(FOLDER, NAME)
WHERE ID = NEW.ID;
END IF;
RETURN NULL;
END
$$
LANGUAGE PLPGSQL;
Trigger
CREATE TRIGGER TRIGGER_LINK
BEFORE INSERT OR UPDATE
ON FILE
FOR EACH ROW
EXECUTE PROCEDURE LINK();
When I insert a value in table like
INSERT INTO FILE VALUES (1, 'C:\', 'doc.pdf');
I have an error message list index out of range because ID number is not yet created and UPDATE query on INSERT can't execute. But if I make an AFTER UPDATE it will run infinitely.
How to run a trigger function on INSERT or UPDATE with a WHERE clause on ID to target only inserted or updated row ? I'm using PostgreSQL 10.14.
Don't use UPDATE to do that. Just assign the value. Also, a BEFORE trigger should not return null because that will abort the operation.
CREATE OR REPLACE FUNCTION LINK() RETURNS trigger AS
$$
BEGIN
new.url := CONCAT(new.FOLDER, new.NAME);
RETURN new; --<< important!
END
$$
LANGUAGE PLPGSQL;

Create Trigger to update a row on same table when insert or update happens using postgresql

I am new to plpgsql and trying the below scenario. It would be helpful if someone could help on this issue.
I have a table named emp_table and whenever an record is inserted, I called a trigger to update a column record_status with 'U' and when insert operation is happened on table, I need to update the record-status column to 'I'
Table :
CREATE TABLE emp_data (
name text,
age integer,
designation text,
salary integer,
last_login TIMESTAMP,
record_status varchar
);
CREATE OR REPLACE FUNCTION em_batch_update()
RETURNS trigger
LANGUAGE PLPGSQL
AS
$$
BEGIN
IF (TG_OP = 'UPDATE') THEN
UPDATE emp_data SET record_status = 'U' WHERE record_status is not null;
RETURN NEW;
ELSIF (TG_OP = 'INSERT') THEN
UPDATE emp_data SET record_status = 'I' WHERE record_status is NULL;
RETURN NEW;
end if;
END;
$$
CREATE TRIGGER em_sem_batch
BEFORE INSERT OR UPDATE ON emp_data
FOR EACH ROW
EXECUTE PROCEDURE em_batch_update();
I have inserted below new record, but the record_status is not getting updated.
insert into emp_data(name,age,designation,salary,last_login) values ('test1234',3,'test1143',1332224,current_timestamp);
I get below error when I either update or insert,
PL/pgSQL function em_batch_update1() line 5 at SQL statement
SQL statement "UPDATE emp_data SET record_status = 'U' WHERE record_status is not null"
PL/pgSQL function em_batch_update1() line 5 at SQL statement
SQL statement "UPDATE emp_data SET record_status = 'I' WHERE record_status is NULL"
PL/pgSQL function em_batch_update1() line 8 at SQL statement
SQL state: 54001
can someone help with this
No need for an UPDATE. In a row-level BEFORE trigger you can simply assign the values:
CREATE OR REPLACE FUNCTION em_batch_update()
RETURNS trigger
LANGUAGE PLPGSQL
AS
$$
BEGIN
IF (TG_OP = 'UPDATE') THEN
new.record_status := 'U';
ELSIF (TG_OP = 'INSERT') THEN
new.record_status := 'I';
end if;
RETURN NEW;
END;
$$
Or even simpler:
CREATE OR REPLACE FUNCTION em_batch_update()
RETURNS trigger
LANGUAGE PLPGSQL
AS
$$
BEGIN
new.record_status := left(TG_OP, 1);
RETURN NEW;
END;
$$

Aftre update deletion of row

I have the following table called 'second'
TABLE public.second
(
userid bigint,
companyid bigint
)
and table called visibility_matrix
CREATE TABLE public.visibility_matrix
(
name character varying,
companyid bigint
)
I created a trigger after I update the 'second' table I want to delete the row in visibility matrix ,I tried to achieve it via deleting the row with the companyid in visibility_matrix but it didn't work,any suggestions? here is the trigger:
CREATE OR REPLACE FUNCTION pos_org_rel_refresh()
RETURNS trigger AS
$$
DECLARE
r Integer ;
BEGIN
IF TG_OP='UPDATE' THEN
DELETE FROM visibility_matrix where companyid=NEW.companyid;
END IF;
END;
$$
LANGUAGE 'plpgsql';
CREATE TRIGGER test_trigger
AFTER UPDATE OR DELETE
ON second
FOR EACH ROW
EXECUTE PROCEDURE pos_org_rel_refresh();
you are missing a return statement, this should fix it:
CREATE OR REPLACE FUNCTION pos_org_rel_refresh() RETURNS trigger AS
$$
DECLARE
r Integer ;
BEGIN
IF TG_OP='UPDATE' THEN
DELETE FROM visibility_matrix where companyid=NEW.companyid;
RETURN NEW;
END IF;
RETURN NULL;
END;
$$
LANGUAGE 'plpgsql';

Postgresql : insert record using trigger

CREATE TABLE users
(
id integer NOT NULL DEFAULT nextval('userseq'::regclass)
........
)
CREATE TABLE History
(
userid integer,
createdat timestamp with time zone
)
CREATE OR REPLACE FUNCTION recordcreatetime()
RETURNS trigger AS
$BODY$
BEGIN
NEW.createdAt = NOW();
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
User and History has one-one relationship. How i can insert new record in History table before creating new user.
CREATE OR REPLACE FUNCTION recordcreatetime()
RETURNS trigger
language plpgsql
AS $$
DECLARE
BEGIN
INSERT INTO History values(new.id,NOW() );
RETURN NEW;
END;
$$;
and write trigger statement as
CREATE TRIGGER user_hist
BEFORE INSERT ON users
FOR EACH ROW EXECUTE function recordcreatetime() ;