I'm trying to implement soft delete with triggers. So that when a record in a table is soft deleted, a trigger handles soft deleting all dependent rows in another table. That is, table B has a foreign key to table A. When I soft delete a record in table A, I want all records in table B with a foreign key to the affected record in table A to also get soft deleted.
The main implementation of the trigger works. But I want to make sure that I don't update any records unless I actually need to. So if a soft deleted record in table A would be updated again for some reason, the records in table B should not be updated.
This is the first implementation that works:
CREATE OR REPLACE FUNCTION TableB_SoftDelete_WhenTableASoftDeleted() RETURNS TRIGGER AS
$$
BEGIN
RAISE NOTICE 'OLD.deleted_at: %', OLD.deleted_at;
RAISE NOTICE 'NEW.deleted_at: %', NEW.deleted_at;
IF NEW.deleted_at IS NOT NULL THEN
RAISE NOTICE 'Will update Table B with a_id %', NEW.id;
UPDATE table_b
SET deleted_at = NEW.deleted_at
WHERE a_id = NEW.id;
ELSE
RAISE NOTICE 'IF didnt match...';
END IF;
RETURN NEW;
END;
$$
LANGUAGE plpgsql;
CREATE OR REPLACE TRIGGER TR_TableA_AU AFTER UPDATE ON table_a
FOR EACH ROW EXECUTE PROCEDURE TableB_SoftDelete_WhenTableASoftDeleted();
So when I do UPDATE table_a SET deleted_at = CURRENT_TIMESTAMP WHERE id = 1;
I get the following log:
NOTICE: OLD.deleted_at: <NULL>
NOTICE: NEW.deleted_at: 2023-02-07 07:51:15.869723
NOTICE: Will update Table B with a_id 1
But this would obviously update Table B any time an already soft deleted record in Table A is updated again.
So then I try to change my condition into IF NEW.deleted_at IS NOT NULL AND NEW.deleted_at != OLD.deleted_at THEN
, but then my IF isn't hit anymore, and I don't understand why.
I see this in the log:
NOTICE: OLD.deleted_at: <NULL>
NOTICE: NEW.deleted_at: 2023-02-07 07:52:31.22866
NOTICE: IF didnt match...
deleted_at
is differing in the OLD and NEW records.
deleted_at
in both tables are of the type timestamp(6) without time zone
.
On many databases, logical operations work like this:
null != '2023-02-07' Returns = false
So it's better if you write like this:
RAISE NOTICE 'OLD.deleted_at: %', OLD.deleted_at;
RAISE NOTICE 'NEW.deleted_at: %', NEW.deleted_at;
IF ((OLD.deleted_at is null) and (NEW.deleted_at IS NOT null)) or (OLD.deleted_at != NEW.deleted_at) THEN
RAISE NOTICE 'Will update Table B with a_id %', NEW.id;
UPDATE table_b
SET deleted_at = NEW.deleted_at
WHERE a_id = NEW.id;
ELSE
RAISE NOTICE 'IF didnt match...';
END IF;
RETURN NEW;