Postgres update column value to time.Now() - postgresql

I need to update column paid_at to time.Now() on table orders when column status is updated to paid is it possible to create a trigger on postgres? they are all in a same table orders

is it possible to create a trigger on postgres
Yes, that's possible.
As documented in the manual you need a trigger function first:
create function update_paid_at()
returns trigger
as
$$
begin
new.paid_at := now();
return new;
end;
$$
language plpglsql;
Then you need a trigger definition:
create trigger update_orders_trigger
before update on orders
for each row
when (new.status = 'paid' and new.status <> old.status)
execute procedure update_paid_at();
The trigger only fires if the status is changed to 'paid' and then sets the value of paid_at column.

Related

SELECT in cascaded AFTER DELETE trigger returning stale data in Postgres 11

I have an AFTER INSERT/UPDATE/DELETE trigger function which runs after any change to table campaigns and triggers an update on table contracts:
CREATE OR REPLACE FUNCTION update_campaign_target() RETURNS trigger AS $update_campaign_target$
BEGIN
UPDATE contracts SET updated_at = now() WHERE contracts.contract_id = NEW.contract_id;
END;
$update_campaign_target$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS update_campaign_target ON campaigns;
CREATE TRIGGER update_campaign_target AFTER INSERT OR UPDATE OR DELETE ON campaigns
FOR EACH ROW EXECUTE PROCEDURE update_campaign_target();
I have another trigger on table contracts that runs BEFORE UPDATE. The goal is to generate a computed column target which displays either contracts.manual_target (if set) or SUM(campaigns.target) WHERE campaign.contract_id = NEW.contract_id.
CREATE OR REPLACE FUNCTION update_contract_manual_target() RETURNS trigger AS $update_contract_manual_target$
DECLARE
campaign_target_count int;
BEGIN
IF NEW.manual_target IS NOT NULL
THEN
NEW.target := NEW.manual_target;
RETURN NEW;
ELSE
SELECT SUM(campaigns.target) INTO campaign_target_count
FROM campaigns
WHERE campaigns.contract_id = NEW.contract_id;
NEW.target := campaign_target_count;
RETURN NEW;
END IF;
END;
$update_contract_manual_target$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS update_contract_manual_target ON contracts;
CREATE TRIGGER update_contract_manual_target BEFORE INSERT OR UPDATE ON contracts
FOR EACH ROW EXECUTE PROCEDURE update_contract_manual_target();
This works as expected on INSERT and UPDATE on campaigns, but does not work on DELETE. When a campaign is deleted, the result of SUM(campaigns.target) in the second trigger includes the deleted campaign's target, and thus does not update the contracts.target column to the expected value. A second update of contracts will correctly set the value.
Three questions:
Why doesn't this work?
Is there a way to achieve the behavior I'm looking for using triggers?
For this type of data synchronization, is it better to achieve this using triggers or views? Triggers make sense to me because this is a table that we will read many magnitudes of times more than we'll write to it, but I'm not sure what the best practices are.
The reason this doesn't work is the usage of NEW.contract_id in the AFTER DELETE trigger:
UPDATE contracts SET updated_at = now() WHERE contracts.contract_id = NEW.contract_id;
Per the Triggers on Data Changes documentation, NEW is NULL for DELETE triggers.
Updating the code to use OLD instead of NEW fixes the issue:
CREATE OR REPLACE FUNCTION update_campaign_target() RETURNS trigger AS $update_campaign_target$
BEGIN
IF TG_OP = 'DELETE'
THEN
UPDATE contracts SET updated_at = now() WHERE contracts.contract_id = OLD.contract_id;
ELSE
UPDATE contracts SET updated_at = now() WHERE contracts.contract_id = NEW.contract_id;
END IF;
RETURN NULL;
END;
$update_campaign_target$ LANGUAGE plpgsql;
Thanks to Anthony Sotolongo and Belayer for your help!

UPDATE ANOTHER COLUMN IN AFTER UPDATE TRIGGER

I have a table with three columns: id, date and dateDekete
I try to execute an update on the column dateDelete after an update on another column (column date) using a AFTER UPDATE TRIGGER.
The code that I use to create my trigger is the following:
CREATE OR REPLACE FUNCTION update_delete_date_allocation()
RETURNS trigger LANGUAGE plpgsql AS $body$
BEGIN
NEW."dateDelete" := NEW.date + 1;
RETURN NEW;
END;
$body$;
CREATE TRIGGER delete_date_allocation_trg
AFTER INSERT OR UPDATE ON client.client_portfolio_allocation
FOR EACH ROW
EXECUTE PROCEDURE update_delete_date_allocation();
Although the code executes fine with no error message, the latter column that I try to update does not change.
I was wondering if it's possible to do this. AND if so, what should I change in my code?
I am using Postgres 11.5.
you can't change the new record in an AFTER trigger, you need to declare your trigger as a BEFORE trigger:
CREATE TRIGGER delete_date_allocation_trg
BEFORE INSERT OR UPDATE ON client.client_portfolio_allocation
FOR EACH ROW
EXECUTE PROCEDURE update_delete_date_allocation();

Updating current timestamp of multiple tables through single trigger function

I have 2 tables created :- Table Banks and Branch. Both the tables have column last_updated(which means when was the record last updated.) I have created after update trigger for each row on both the tables. The trigger and trigger function looks like below :-
create trigger banks_upd_trg
after update of phone_no
on Banks
FOR EACH ROW
EXECUTE PROCEDURE bankdetails_upd();
create trigger branch_upd_trg
after update of email_address
on Branch
FOR EACH ROW
EXECUTE PROCEDURE bankdetails_upd();
create or replace FUNCTION bankdetails_upd()
RETURNS trigger AS
$BODY$
BEGIN
EXECUTE format('update %I.%I SET last_updated=current_timestamp where id=new.id',TG_SHEMA_NAME,TG_TABLE_NAME)
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql
The trigger is getting executed successfully but isn't working at the time of updating phone_no and email_address column in Banks and Branch table respectively.
No need for dynamic SQL or an UPDATE statement.
Use a before trigger and assign the value to the NEW record.
create or replace FUNCTION bankdetails_upd()
RETURNS trigger AS
$BODY$
BEGIN
new.last_updated := current_timestamp;
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql;
create trigger banks_upd_trg
BEFORE update of phone_no
on Banks
FOR EACH ROW
EXECUTE PROCEDURE bankdetails_upd();
create trigger branch_upd_trg
BEFORE update of email_address
on Branch
FOR EACH ROW
EXECUTE PROCEDURE bankdetails_upd();

PostgreSQL: Checking for NEW and OLD in a function for a trigger

I want to create a trigger which counts rows and updates a field in an other table. My current solution works for INSERT statements but failes when I DELETE a row.
My current function:
CREATE OR REPLACE FUNCTION update_table_count()
RETURNS trigger AS
$$
DECLARE updatecount INT;
BEGIN
Select count(*) into updatecount
From source_table
Where id = new.id;
Update dest_table set count=updatecount
Where id = new.id;
RETURN NEW;
END;
$$
LANGUAGE 'plpgsql';
The trigger is a pretty basic one, looking like.
CREATE TRIGGER count_trigger
AFTER INSERT OR DELETE
ON source_table
FOR EACH ROW
EXECUTE PROCEDURE update_table_count();
When I excute a DELETE statement the following error occurs:
ERROR: record "new" is not assigned yet
DETAIL: The tuple structure of a not-yet-assigned record is indeterminate.
I know one solution could be to create just one set of trigger and function for the DELETE and one for the INSERT statement. But I want to do it a bit more elegant and want to know, if there is a solution to check if NEW or OLD is present in the current context and just implement an IF ELSE block. But I dont know how to check for this context sensitive items.
Thanks for your help
The usual approach to make a trigger function do different things depending on how the trigger was fired is to check the trigger operation through TG_OP
CREATE OR REPLACE FUNCTION update_table_count()
RETURNS trigger AS
$$
DECLARE
updatecount INT;
BEGIN
if tg_op = 'UPDATE' then
select count(*) into updatecount from source_table where id = new.id;
update dest_table set count=updatecount where id = new.id;
elsif tg_op = 'DELETE' then
... do something else
end if;
RETURN NEW;
END;
$$
LANGUAGE plpgsql;
Unrelated, but: the language name is an identifier. Do not quote it using single quotes.
From PostgreSQL's documentation:
NEW
Data type RECORD; variable holding the new database row for INSERT/UPDATE operations in row-level triggers. This variable is null in statement-level triggers and for DELETE operations.
OLD
Data type RECORD; variable holding the old database row for UPDATE/DELETE operations in row-level triggers. This variable is null in statement-level triggers and for INSERT operations.
So, for example, if NEW is NULL, then the trigger was invoked on DELETE.

Triger update function when column updates in Postgres

I have users table with columns status_id (int), additional_status(int) and status_changed(DATE).
I want to autoupdate status_changed field every time when status_id or additional_status changes.
Here is what I have by now:
CREATE OR REPLACE FUNCTION update_status_changed()
RETURNS TRIGGER
AS $$
BEGIN
NEW.status_changed := CURRENT_TIMESTAMP;
RETURN NEW;
END $$ LANGUAGE plpgsql;
CREATE TRIGGER set_update_status_changed
AFTER INSERT OR UPDATE OF status_id, additional_status ON users
FOR EACH ROW
EXECUTE PROCEDURE update_status_changed();
I'm not sure if the syntax is correct. When I change my status_id from phpPgAdmin - status_changed stays NULL. What am I'm missing?, help pls.