PostgreSQL trigger after update only on a updated row - sql

I have a small table for news. I want to make a trigger which sets the update date and update time in the row (only for the rows that were updated)
I tried making the following:
CREATE FUNCTION add_update_dates()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $$
BEGIN
IF (OLD.news_name IS DISTINCT FROM NEW.news_name OR
OLD.news_description IS DISTINCT FROM NEW.news_description OR
OLD.news_text IS DISTINCT FROM NEW.news_text) THEN
UPDATE news SET news_update_date = current_date, news_update_time = current_time;
END IF;
RETURN new;
END
$$;
CREATE TRIGGER update_news_dates
AFTER UPDATE ON news
FOR EACH ROW
EXECUTE PROCEDURE add_update_dates();
But the trigger updates each row in my table (even those that are not updated), when I want only the updated ones. What am I doing wrong?

Your update statement is updating all the rows in the table! It has no where clause.
Just use assignment:
CREATE FUNCTION add_update_dates()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $$
BEGIN
IF (OLD.news_name IS DISTINCT FROM NEW.news_name OR
OLD.news_description IS DISTINCT FROM NEW.news_description OR
OLD.news_text IS DISTINCT FROM NEW.news_text
) THEN
NEW.news_update_date := current_date;
NEW.news_update_time := current_time;
END IF;
RETURN new;
END;
$$;
As an aside, storing date/time in separate columns makes no sense to me.

Related

Update columns for the first user inserted

I'm trying to create a trigger and a function to update some columns (roles and is_verified) for the first user created. Here is my function :
CREATE OR REPLACE FUNCTION public.first_user()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
DECLARE
begin
if(select count(*) from public.user = 1) then
update new set new.is_verified = true and new.roles = ["ROLE_USER", "ROLE_ADMIN"]
end if;
return new;
end;
$function$
;
and my trigger :
create trigger first_user
before insert
on
public.user for each row execute function first_user()
I'm working on Dbeaver and Dbeaver won't persist my function because of a syntax error near the "=". Any idea ?
Quite a few things are wrong in your trigger function. Here it is revised w/o changing your business logic.
However this will affect the second user, not the first. Probably you shall compare the count to 0. Then the condition shall be if not exists (select from public.user) then
CREATE OR REPLACE FUNCTION public.first_user()
RETURNS trigger LANGUAGE plpgsql AS
$function$
begin
if ((select count(*) from public.user) = 1) then
-- probably if not exists (select from public.user) then
new.is_verified := true;
new.roles := array['ROLE_USER', 'ROLE_ADMIN'];
end if;
return new;
end;
$function$;

PostgreSQL Trigger update all row in the same table

I want calculate a field when update one or two fields but Trigger calculate all table's row with same value, not only the row updated. What is wrong ..? Thank you.
Create or Replace Function hospital_ocupation() returns Trigger
as
$$
Begin
update hospital set p_ocupation = (new.n_cases*100)/new.capacity
where old.n_case <> new.n_case;
return new;
End
$$
language plpgsql;
Create Trigger update_ocupation after update on hospital
for each row
WHEN (pg_trigger_depth() = 0)
execute procedure hospital_ocupation();
update hospital set capacity=500,n_cases=50
where id_hospital = 1
enter image description here
Don't use UPDATE, assign the calculated value to the new record in a BEFORE trigger:
create or replace function hospital_ocupation()
returns trigger
as
$$
begin
new.p_ocupation := (new.n_cases*100)/new.capacity;
return new;
End
$$
language plpgsql;
To make that work you need a BEFORE trigger:
create trigger update_ocupation
BEFORE update on hospital
for each row
WHEN (old.n_case <> new.n_case)
execute procedure hospital_ocupation();

Trigger for update timestamp field

I'm trying to create trigger, that updates my lastmodified field on insert/update. Here's my trigger:
CREATE OR REPLACE TRIGGER timestamp_trigger BEFORE INSERT OR UPDATE ON orders
FOR EACH ROW EXECUTE PROCEDURE timestamp_update;
and my procedure:
CREATE OR REPLACE FUNCTION timestamp_update()
RETURNS TRIGGER
AS
$$
BEGIN
UPDATE orders
SET lastmodified = getdate()
WHERE orderid = new.orderid
RETURN new;
END;
$$ language plpgsql;
Unfortunately when I try to update some field, field isn't updated. Also when i create row, the lastmodified field isn't updated.
Do not use update in a trigger like this because it may lead to endless recursive loop.
If you want to modify inserted/updated row, just change the new record:
CREATE OR REPLACE FUNCTION timestamp_update()
RETURNS TRIGGER
AS
$$
BEGIN
NEW.lastmodified = now();
RETURN new;
END;
$$ language plpgsql;
The syntax create or replace trigger is incorrect.
CREATE TRIGGER timestamp_trigger
BEFORE INSERT OR UPDATE ON orders
FOR EACH ROW EXECUTE PROCEDURE timestamp_update();

SQL function, substract variable on another table when insert data

I have a relational database:
Examination (ExaminationID, DetectedDisease, Name)
Uses (ReagentID, ExaminationID, ReagentQuantity)
Reagent (ReagentID, Name, Stock, MinimumPermissibleStock)
Examined (InsuranceNumber, ExaminationID, ExaminationDate, Results, TakenResults?)
I want to create a function that when a new record of "examined" is added the quantity of the reagent that examination uses to be substracted from "reagent.stock". Also when reagent.minimumPermissibleStock is lower than stock to return a warning message.
For the second part of the problem i tried this, but it does not work:
create function warning() returns trigger AS $warning$
begin
if reagent.new.stock < reagent.minimumPermissibleStock then
raise exception 'Probably mistake';
end if;
return new;
end;
$warning$ LANGUAGE plpgsql;
Thanks in advance
EDIT
I tried that one but still it doesnt work:
CREATE FUNCTION log_examination2() RETURNS trigger AS $$
DECLARE
examID integer;
BEGIN
BEGIN
INSERT INTO examination (detecteddisease, ename) VALUES('disease1', 'name') RETURNING examinationID INTO examID;
INSERT INTO Uses VALUES(reagentID, examID, reagentQuantity);
UPDATE Reagent SET Stock = Stock - reagentQuantity WHERE ReagentID = reagentID;
RETURN new;
END;
END;
$$ LANGUAGE plpgsql VOLATILE;
CREATE TRIGGER validate_quantity AFTER UPDATE OF Stock ON Reagent EXECUTE PROCEDURE log_examination2();
Triggers can only operate on the table/view to which they are attached--hence the error (I'm assuming you created the trigger as an AFTER UPDATE on Examined).
To actually solve the problem, what you want to do is create a transaction to represent the entire set of operations, and then attach the above function as a trigger AFTER UPDATE to Reagent.
For example:
CREATE FUNCTION log_examination(int reagentID, int reagentQuantity) AS $$
BEGIN
BEGIN;
examID := INSERT INTO Examination VALUE("disease1", "name") RETURNING ExaminationID;
INSERT INTO Uses VALUE(reagentID, examID, reagentQuantity);
UPDATE Reagent SET Stock = Stock - reagentQuantity WHERE ReagentID = reagentID;
COMMIT;
END;
$$ LANGUAGE plpgsql;
and
CREATE TRIGGER validate_quantity AFTER UPDATE OF Stock ON Reagent EXECUTE PROCEDURE warning();

SQL, update column in a specific row, instead of all rows

The task is to update the specific row in the column klienta_nr, which is located in the table klientu_ieteikumi. If a specific row is deleted in klienti table. This code updates the whole column not the specific row. What is the problem?
CREATE FUNCTION "funkc"() RETURNS "opaque" AS '
DECLARE
BEGIN
IF (TG_OP = ''DELETE'') THEN
UPDATE klientu_ieteikumi SET klienta_nr = NULL ;
END IF;
RETURN NEW;
END;
' LANGUAGE 'plpgsql';
CREATE TRIGGER "triger"
AFTER DELETE ON "klienti"
FOR EACH ROW EXECUTE PROCEDURE funkc();
This one did what I wanted, thanks anyway everyone :)
CREATE FUNCTION "funkcija1"() RETURNS TRIGGER AS $$
BEGIN
UPDATE klientu_ieteikumi SET klienta_nr = NULL
FROM klienti WHERE old.klienta_nr = klientu_ieteikumi.klienta_nr;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER "trigeris"
AFTER DELETE ON "klienti"
FOR EACH ROW EXECUTE PROCEDURE funkcija1();
you need a WHERE clause to restrict the number of rows affected by the statement.
edit:
UPDATE klientu_ieteikumi
SET klienta_nr = NULL
WHERE klienta_ieteikumi.klienta_nr = klienti.klienta_nr
You presented "solution" cannot work and is wrong in a sneaky way.
CREATE FUNCTION funkcija1()
RETURNS TRIGGER AS
$func$
BEGIN
CREATE FUNCTION funkcija1()
RETURNS TRIGGER AS
$func$
BEGIN
UPDATE klientu_ieteikumi
SET klienta_nr = NULL
FROM klienti -- !!
WHERE klientu_ieteikumi.klienta_nr = OLD.klienta_nr;
RETURN NEW;
RETURN NULL;
END
$func$ LANGUAGE plpgsql;
CREATE TRIGGER trigeris
AFTER DELETE ON klienti
FOR EACH ROW EXECUTE PROCEDURE funkcija1();
There is no NEW in an AFTER trigger. This wouldn't work at all and raise an exception immediately.
The unbound (and utterly pointless) table klienti in the FROM clause leads to a CROSS JOIN. I.e., instead of just one time, the UPDATE is executed as many times as there are rows in klienti. This would be a major drag on performance, and you might never find out, since there is no error message. Just a lot of wasted cycles and table bloat on your server.