If you want the delete to happen immediately, then you can declare a trigger function like so...
CREATE FUNCTION check_ref_count()
RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
DELETE FROM Refe WHERE id NOT IN (SELECT refe from liverefe);
RETURN NULL;
END;
$$;
And then attach it to the tables in question.
CREATE TRIGGER slay_orphans
AFTER DELETE ON Foo
FOR EACH STATEMENT
EXECUTE PROCEDURE check_ref_count();
CREATE TRIGGER slay_orphans
AFTER DELETE ON Bar
FOR EACH STATEMENT
EXECUTE PROCEDURE check_ref_count();
(Depending on how you're working with these items, you might have to say AFTER DELETE OR UPDATE
rather than just AFTER DELETE
. Particularly if you change refe
a lot. If you typically set it once and leave it alone, AFTER DELETE
should be fine.)
Note: once you do this, a record in Refe
won't survive the next deletion unless some record in liverefe
has its ID. Even though that's what you want, it means that if deletions are common, even brand new records might get deleted right out from under you.
You can make things stricter by only checking Refe
s that deleted rows were pointing at. Just add id = old.refe
to the delete conditions, and make the triggers FOR EACH ROW
rather than FOR EACH STATEMENT
. This'll make it so that only previously-pointed-at records are candidates for deletion. (But it also means that cleanup is not retroactive -- current orphans won't get cleaned up unless something points at them and then points elsewhere or gets deleted. And if you go this route, AFTER DELETE OR UPDATE
is essential -- the sloppiness of letting the delete trigger handle everything, causes rows to basically leak if you're not cleaning up the whole table.)
Or, add a timestamp to Refe
, and change the query to ignore records whose timestamp is less than X seconds/minutes/whatever ago.
However you resolve that issue, you'll probably also want to consider using transactions
to help ensure that a record's not getting deleted right as you're trying to point at it. And make sure your foreign keys are specified as ON DELETE RESTRICT
(so that deleting a record in Refe
won't be allowed unless the record is indeed an orphan).
If you could live with rows getting deleted every so often rather than immediately, it gets easier still, since you don't have to create a function and such.
Just create a script that issues this query:
DELETE FROM Refe WHERE id NOT IN (SELECT refe FROM liverefe)
and set that up as a cron job (or scheduled task, in Windows).