给出带有以下表格的postgreSQL查询:
+--------------------+
|Foo |
+--------------------+
| id | value1 | refe |
+--------------------+
+--------------------+
|Bar |
+--------------------+
| id | value2 | refe |
+--------------------+
+-------------+
|Refe |
+-------------+
| id | value3 |
+-------------+
refe
列是id
Refe
的外键。现在Refe
存储了与Foo
和Bar
(以及可能的其他表格)相关的其他数据。
我创建了一个存储"活跃"的视图。换句话说,refe
:refe
和Foo
的{{1}}字段的并集:
Bar
现在当一个人从CREATE OR REPLACE VIEW liverefe AS
(SELECT refe FROM Foo)
UNION (SELECT refe FROM Bar)
或Foo
删除一行时,Bar
可能会死亡(没有其他refe
或Foo
行指它)。在这种情况下,它应该删除。
可以解释一下如何实现这样的"引用计数"触发?
答案 0 :(得分:2)
使用NOT EXISTS
反半连接。
删除refe
中的所有死行:
DELETE FROM refe r
WHERE NOT EXISTS (SELECT 1 FROM foo WHERE refe = r.id)
AND NOT EXISTS (SELECT 1 FROM bar WHERE refe = r.id);
(可能)在其中一个引用表中删除DELETE
/ UPDATE
之后的refe中的特定行:
DELETE FROM refe r
WHERE r.id = 12345 -- your id here
AND NOT EXISTS (SELECT 1 FROM foo WHERE refe = r.id)
AND NOT EXISTS (SELECT 1 FROM bar WHERE refe = r.id);
此类触发器的完整代码示例的相关答案:
如果您应该更新refe
列,请添加如下触发器:
CREATE TRIGGER foo_updelaft_kill_orphaned_refe
AFTER DELETE OR UPDATE OF refe
ON foo
FOR EACH ROW EXECUTE PROCEDURE f_trg_kill_orphaned_refe();
表格bar
等相同
这样,仅当UPDATE
实际定位refe
列时才会调用触发器。
答案 1 :(得分:2)
如果你想立即删除,那么你可以声明一个触发器函数......
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;
$$;
然后将其附加到相关表格中。
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();
(根据您使用这些项目的方式,您可能不得不说AFTER DELETE OR UPDATE
而不仅仅是AFTER DELETE
。特别是如果您经常更改refe
。如果您通常设置它曾经并且不管它,AFTER DELETE
应该没问题。)
注意:执行此操作后,Refe
中的记录将无法在下次删除时继续存在,除非liverefe
中的某些记录具有其ID。尽管这就是你想要的,但这意味着如果删除很常见,即使是全新的记录也可能会被删除。
只需检查已删除行指向的Refe
,就可以使事情变得更严格。只需将id = old.refe
添加到删除条件,然后设置触发器FOR EACH ROW
而不是FOR EACH STATEMENT
。这样做只会使以前指出的记录成为删除的候选者。 (但这也意味着清理不会追溯 - 当前的孤儿不会被清除,除非有东西指向他们然后指向别处或被删除。如果你走这条路线,AFTER DELETE OR UPDATE
是必不可少的 - 让删除触发器处理所有内容的邋iness,如果你没有清理整个表,会导致行基本泄漏。)
或者,向Refe
添加时间戳,并更改查询以忽略时间戳小于X秒/分钟/之前的记录。
但是,您解决了这个问题,您可能还想考虑使用交易
帮助确保记录在您尝试指向时不会被删除。并确保将您的外键指定为ON DELETE RESTRICT
(这样除非记录确实是一个孤儿,否则将不允许删除Refe
中的记录。)
如果您可以将所有行经常删除而不是立即删除,那么它会变得更容易,因为您不必创建函数等。
只需创建一个发出此查询的脚本:
DELETE FROM Refe WHERE id NOT IN (SELECT refe FROM liverefe)
并将其设置为cron作业(或Windows中的计划任务)。