在我的数据库中,我有tasks
和comments
个表。每项任务都有很多评论。
我想创建tasks.comments_count
列,它将由PostgreSQL自动更新,因此我可以在O(1)时间内选择所有任务时获取comments_count
(并按其排序/过滤)
我知道有一些特定于语言的解决方案,比如ActiveRecord的counter cache,但我不想使用它们(我觉得它们很脆弱)。我希望PostgreSQL能够处理这样的反缓存。
我也知道PostgreSQL支持触发器,但它们很难编写和使用(不是一个可靠的解决方案)
理想情况下,它将是PostgreSQL扩展或我不知道的一些本机功能。
懒惰计算这样的计数器将是一个很大的好处。
答案 0 :(得分:18)
如果你希望Postgres在插入/更新/删除的基础上自动执行某些操作 - 即如果你希望此操作触发某些其他操作 - 那么你需要编写一个触发器。 / p>
这很简单。很简单,我怀疑有人会打扰创建一个扩展(更不用说语言功能)来节省你的麻烦。它确实比ActiveRecord在幕后所做的更简单(而且正如你所指出的那样更安全)。
这样的事情通常都需要(我没有对此进行过测试,所以你可能会这样做......):
CREATE FUNCTION maintain_comment_count_trg() RETURNS TRIGGER AS
$$
BEGIN
IF TG_OP IN ('UPDATE', 'DELETE') THEN
UPDATE tasks SET comment_count = comment_count - 1 WHERE id = old.task_id;
END IF;
IF TG_OP IN ('INSERT', 'UPDATE') THEN
UPDATE tasks SET comment_count = comment_count + 1 WHERE id = new.task_id;
END IF;
RETURN NULL;
END
$$
LANGUAGE plpgsql;
CREATE TRIGGER maintain_comment_count
AFTER INSERT OR UPDATE OF task_id OR DELETE ON comments
FOR EACH ROW
EXECUTE PROCEDURE maintain_comment_count_trg();
如果您希望它是密不透气的,那么TRUNCATE
上的comments
s需要额外的触发器;是否值得给你带来麻烦。
要处理正被引用的tasks.id
值的更新(通过延迟约束或ON UPDATE
操作),那么还有更多内容,但这是一种不常见的情况。< / p>
如果您的客户端库/ ORM足够天真地发送每个UPDATE
语句中的每个字段,您可能需要一个单独的UPDATE
触发器,仅在值实际更改时触发:
CREATE TRIGGER maintain_comment_count_update
AFTER UPDATE OF task_id ON comments
FOR EACH ROW
WHEN (old.task_id IS DISTINCT FROM new.task_id)
EXECUTE PROCEDURE maintain_comment_count_trg();