Postgres - ´after update´ trigger doesn't fire - postgresql

I have this table for which one of the columns is to be filled with values from other columns of the same table (these other columns represent a taxonomic hierarchy whose lowest level I wish to store in this other column).
To achieve this I implemented the following trigger:
CREATE OR REPLACE FUNCTION get_taxon()
RETURNS TRIGGER LANGUAGE plpgsql AS
$BODY$
BEGIN
UPDATE taxon SET taxon = coalesce(subespecie, especie, genero, subfamilia, familia, infraordem, subordem, ordem, superordem, subclasse, classe, subphylum, phylum, reino )
WHERE taxon IS NULL;
RETURN NEW;
END
$BODY$
VOLATILE;
CREATE TRIGGER update_taxon
AFTER INSERT OR UPDATE ON taxon
FOR EACH STATEMENT
WHEN (pg_trigger_depth() = 0) -- Prevent recursive trigger calls
EXECUTE PROCEDURE get_taxon();
When I insert a new record the trigger works as expected, but if I update an existing record, nothing happens - the trigger just ignores UPDATE operations and I don't know why.
Can anyone shed some light on this please?

After your statement trigger first fired all records in the table should have their taxon field updated with the best available information. When you update a record you may want to update the taxon value, but you are better off with a BEFORE INSERT OR UPDATE FOR EACH ROW trigger. The only new data is contained in the row for which the trigger fires, of course. So try this:
CREATE OR REPLACE FUNCTION get_taxon()
RETURNS TRIGGER LANGUAGE plpgsql AS
$BODY$
BEGIN
NEW.taxon := coalesce(NEW.subespecie, NEW.especie, NEW.genero, NEW.subfamilia,
NEW.familia, NEW.infraordem, NEW.subordem, NEW.ordem, NEW.superordem,
NEW.subclasse, NEW.classe, NEW.subphylum, NEW.phylum, NEW.reino);
RETURN NEW;
END;
$BODY$ VOLATILE;
CREATE TRIGGER update_taxon
BEFORE INSERT OR UPDATE ON taxon
FOR EACH ROW EXECUTE PROCEDURE get_taxon();

Related

Inserting Data in Postgres table when another table has been inserted with multiple rows

I am new to Postgres. My problem is that I want to insert some new rows in "target table" when these new rows are inserted in a "source table".
I wrote a trigger to do exactly that but whenever the source is inserted with say 7 new rows then the trigger inserts 7x7 = 49 rows in target. Next if I insert 3 more new rows in source then the target becomes 49+3x10 = 79.
What am I doing wrong..??
Trigger function:
CREATE OR REPLACE FUNCTION public.rec_insert()
RETURNS trigger AS
$BODY$
BEGIN
INSERT INTO target_table ("TIME","REGION","CITY","DISTRICT","Population")
SELECT NEW."TIME",NEW."REGION",NEW."CITY",NEW."DISTRICT",(100*(NEW."SAMPLES_MALE_Available")/(NULLIF((NEW."Total_AVAIL"-NEW."Female_AVAIL"),0)))
FROM source_table;
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql;
And my trigger is
CREATE TRIGGER ins_same_rec
AFTER UPDATE
ON source_table
FOR EACH ROW
EXECUTE PROCEDURE rec_insert();
You must omit the FROM. Thats the reason because many rows are inserted (one for each row in the previous state of the table)
CREATE OR REPLACE FUNCTION public.rec_insert()
RETURNS trigger AS
$BODY$
BEGIN
INSERT INTO target_table ("TIME","REGION","CITY","DISTRICT","Population")
SELECT NEW."TIME",NEW."REGION",NEW."CITY",NEW."DISTRICT",(100*(NEW."SAMPLES_MALE_Available")/(NULLIF((NEW."Total_AVAIL"-NEW."Female_AVAIL"),0)));
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql;

Postgres insert or update trigger WHEN condition (old)

I need write insert or update trigger, but with WHEN condition with compare OLD and NEW rows.
According documentation OLD is null for insert operation. How i can use OLD in WHEN condition for INSERT AND UPDATE triggers?
Example trigger:
CREATE TRIGGER mytrigger
BEFORE INSERT OR UPDATE ON "mytable"
FOR EACH ROW
WHEN (NEW.score > 0 AND OLD.score <> NEW.score)
EXECUTE PROCEDURE mytrigger();
but for insert OLD is null.
Option A:
You can change the code so that conditions will be in the trigger function rather than the trigger itself. With this approach OLD will be used only in the UPDATE.
Trigger:
CREATE TRIGGER mytrigger
BEFORE INSERT OR UPDATE ON "mytable"
FOR EACH ROW
EXECUTE PROCEDURE mytrigger();
Trigger function:
CREATE OR REPLACE FUNCTION mytrigger()
RETURNS trigger AS
$BODY$
begin
if NEW.score > 0 then
--code for Insert
if (TG_OP = 'INSERT') then
YOUR CODE
end if;
--code for update
if (TG_OP = 'UPDATE') then
if OLD.score <> NEW.score then -- (if score can be null see #voytech comment to this post)
YOUR CODE
end if;
end if;
end if;
return new;
end;
$BODY$
LANGUAGE plpgsql VOLATILE
Option B:
As Thilo suggested write two triggers that share the same trigger function.
Triggers:
CREATE TRIGGER mytrigger1
BEFORE INSERT ON "mytable"
FOR EACH ROW
WHEN NEW.score > 0
EXECUTE PROCEDURE mytrigger();
CREATE TRIGGER mytrigger2
BEFORE UPDATE ON "mytable"
FOR EACH ROW
WHEN (NEW.score > 0 AND OLD.score <> NEW.score)
EXECUTE PROCEDURE mytrigger();
Trigger function:
CREATE OR REPLACE FUNCTION mytrigger()
RETURNS trigger AS
$BODY$
begin
YOUR CODE
return new;
end;
$BODY$
LANGUAGE plpgsql VOLATILE
Trigger.oldMap.keySet(); will give the Id's present in the Trigger.oldMap.
It is a set type collection of all the Id's.
have a look at the following example, change the DML events in the trigger events every time and see the debug logs.
you will understand which trigger context variable is available for which DML event.
CREATE TRIGGER Email_Check_On_Contact
before update{
oldMap<ID,Contact>=new Map<ID,Contact>();
o = trigger.oldMap;
for(Contact newcont: trigger.new)
{
if(newcont.Email != o.get(newcont.Id).Email)
{
newcont.Email.addError('Email cannot be changed');
}
}
}

How to apply a update after an inser or update POSTGRESQL Trigger

How to apply an update after an insert or update in POSTGRESQL; I have got a table which has a field lastupdate; I want that field to be set up whenever the row is updated or when it was inserted.
I tried this trigger, but It is not working! HELP!!
CREATE OR REPLACE FUNCTION fn_update_profile()
RETURNS TRIGGER AS $update_profile$
BEGIN
IF (TG_OP = 'INSERT' OR TG_OP = 'UPDATE' ) THEN
UPDATE profile SET lastupdate=now() where oid=OLD.oid;
RETURN NULL;
ELSEIF (TG_OP = 'DELETE') THEN
RETURN NULL;
END IF;
RETURN NULL; -- result is ignored since this is an AFTER trigger
END;
$update_profile$ LANGUAGE plpgsql;
Your trigger function can be a lot easier than you had. Keep in mind that PG will do the update or the insert on the original table, you only have to deal with keeping the profile table up-to-date:
CREATE OR REPLACE FUNCTION fn_update_profile()
RETURNS TRIGGER AS $update_profile$
BEGIN
UPDATE profile SET lastupdate = now() WHERE oid = NEW.oid;
RETURN NEW;
END;
$update_profile$ LANGUAGE plpgsql;
The INSERT and UPDATE trigger functions both use the NEW parameter; the INSERT trigger function does not have the OLD parameter. You should always return NEW from the trigger function if successful (or OLD from a DELETE trigger), even if it is an AFTER INSERT OR UPDATE trigger; the whole operation will be rolled back if NULL is returned. If you then define the actual trigger to fire after the insert or update, you should be good:
CREATE TRIGGER tr_update_profile
AFTER INSERT OR UPDATE ON my_table
FOR EACH ROW EXECUTE PROCEDURE fn_update_profile();

postgresql-using trigger to fire

I want to create a trigger so that whenever I make a change (Update or Delete) it should copy the old data to a new table (with same template).
I tried this code:
create table restrictions(ID int,name text);
insert into restrictions values(122,'suresh');
select * from restrictions;
create table restrictions_deleted(ID int,name text);// this is my duplicate table for keeping information of all updations.
CREATE OR REPLACE FUNCTION moveDeleted() RETURNS trigger AS $$
BEGIN
INSERT INTO restrictions_deleted VALUES(OLD.ID, OLD.name);
RETURN OLD;
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE TRIGGER moveDeleted
BEFORE DELETE ON restrictions
FOR EACH ROW
EXECUTE PROCEDURE moveDeleted();
delete from restrictions where ID=122;
select * from restrictions_deleted;
This code is capable of recording all the deleted data into duplicate table. But I want to do same for updates also.
Any suggestion, any idea?
First - in a trigger function you need to RETURN NEW; instead of RETURN OLD;.
Second - change the trigger to BEFORE DELETE OR UPDATE.
Last - it is better to have AFTER DELETE OR UPDATE for a logging trigger. This way it wont do useless work, when the change is rolled back.
BTW here is a good example of logging/audit trigger.
UPDATE:
The function will look like:
CREATE OR REPLACE FUNCTION moveDeleted() RETURNS trigger AS $$
BEGIN
IF (TG_OP = 'UPDATE') THEN
INSERT INTO restrictions_deleted VALUES(OLD.ID, OLD.name);
RETURN NEW;
ELSIF (TG_OP = 'DELETE') THEN
INSERT INTO restrictions_deleted VALUES(OLD.ID, OLD.name);
RETURN OLD;
END IF;
END;
$$ LANGUAGE plpgsql;

Postgres trigger function

I need help in Postgres triggers.
I have table with 2 columns:
sold boolean;
id_shop int;
It stores if item is sold, or at which shop its located at.
I need a trigger, if I change the "sold" to true, then it also changes the id_shop to NULL (It can't be in shop if sold...)
I tried different ways, but it doesn't work or gives an error on update cmd...
create or replace function pardota_masina_veikals() RETURNS trigger AS $pardota_masina$
begin
IF NEW.sold=true THEN
update masinas
SET id_shop=null WHERE id=NEW.id;
END IF;
RETURN NEW;
END;
$pardota_masina$ LANGUAGE plpgsql;
CREATE TRIGGER pardota_masina_nevar_but_veikala
AFTER INSERT OR UPDATE ON masinas FOR EACH ROW EXECUTE PROCEDURE pardota_masina_veikals();
First of all you need a before trigger if you want to change a value of the row being updated (or inserted)
Secondly you don't need to "update" the table, just assign the new value to the NEW row:
create or replace function pardota_masina_veikals()
RETURNS trigger
AS
$pardota_masina$
begin
IF NEW.sold=true THEN
NEW.id_shop = NULL;
END IF;
RETURN NEW;
END;
$pardota_masina$
LANGUAGE plpgsql;
CREATE TRIGGER pardota_masina_nevar_but_veikala
BEFORE INSERT OR UPDATE ON masinas
FOR EACH ROW EXECUTE PROCEDURE pardota_masina_veikals();