在 PostgreSQL 中触发

Posted

技术标签:

【中文标题】在 PostgreSQL 中触发【英文标题】:Trigger in PostgreSQL 【发布时间】:2011-02-01 15:19:09 【问题描述】:

我有表 "Candidates"id(主键)和 application_counter 以及带有外键 (candidate_id) 的表 "Applications"。我希望每次添加或删除应用程序时都修改application_counter(或通过更改candidate_id 进行修改)。

我能做的就是写:

CREATE TRIGGER myTrigger AFTER INSERT OR DELETE OR UPDATE
ON "Applications" FOR EACH ROW
EXECUTE PROCEDURE funcname ( arguments )

问题是如何编写这个触发器?

来自http://www.postgresql.org/docs/8.1/interactive/sql-createtrigger.html页面的概要

CREATE TRIGGER name  BEFORE | AFTER   event [ OR ... ] 
ON table [ FOR [ EACH ]  ROW | STATEMENT  ]
EXECUTE PROCEDURE funcname ( arguments )

【问题讨论】:

好的,请问有什么问题?顺便说一句,触发函数不能接受参数,请查看手册:postgresql.org/docs/current/interactive/plpgsql-trigger.html 【参考方案1】:

我会使用一个视图INNER JOIN 这两个表,并计算applications 表中的行数。 (请参阅COUNT()。)可以禁用触发器;这种观点总会给你正确的答案。

(稍后……)

我了解到您希望将候选人在“申请”表中的行数限制为 3 行或更少。在这种情况下,我认为最好在“应用程序”上使用CHECK() 约束,而不是在“应用程序”上使用触发器和在“候选人”上使用CHECK() 约束。

要在 PostgreSQL 中执行此操作,您必须使用一个函数,并从 CHECK() 调用该函数。 (据我所知。你仍然不能在CHECK() 约束中执行任意SELECT 语句,对吧?)所以,你会创建这个函数,

CREATE FUNCTION num_applications(cand_id integer)
  RETURNS integer AS
$BODY$ 

  DECLARE 
    n integer; 
  BEGIN 
    select count(*) 
    into n 
    from applications
    where (candidate_id = cand_id);

    return n; 
  END; 
$BODY$ 
LANGUAGE plpgsql;

然后您将向“应用程序”表添加 CHECK() 约束。

ALTER TABLE applications
  ADD CONSTRAINT max_of_three CHECK (num_applications(candidate_id) < 3);

"CHECK() 约束在添加行之前被评估。可能值得测试一下它在延迟约束下的行为。如果以后有时间,我会做的。

【讨论】:

+1 在这里触发似乎有点过头了(除非是为了训练目的) 我需要触发器,因为application_counter 上有 CHECK 条件,表明每个候选人最多可以发布 3 个申请。 您也可以在触发器中使用 SELECT count()...如果candidate_id 上有一个有用的索引也会很快。 “有用”是什么意思? @Miko Kronn:在这种情况下,我宁愿在表Applications 上使用 CHECK() 约束。我将编辑我的答案以展示如何做到这一点。【参考方案2】:
CREATE TRIGGER myname AFTER INSERT, DELETE OR UPDATE 
ON table applications
EXECUTE PROCEDURE myfunc();

CREATE FUNCTION myfunc() RETURNS TRIGGER AS 
BEGIN
    IF TG_OP != 'DELETE' THEN
        update candicate set application_count = application_count + 1 where id = new.candidate_id;
    END IF;
    IF TG_OP != 'INSERT' THEN
        update candicate set application_count = application_count + 1 where id = old.candidate_id;
    END IF;
END;

我希望您能理解...现在也可以使用更新后的 Candidate_id。

【讨论】:

如何编写这个函数来做我想做的事?我的意思是,我怎样才能知道“候选人”受到影响。可以有一个(插入或删除)或两个(更新)。 在“myFunc()”内,对于插入端,“更新候选集计数器 = 计数器 + 1 其中 id = new.candidate_id”。对于删除触发器,请参阅“old.candidate.id”。除非可以更改 Candidate_id 字段,否则更新不会做任何事情,在这种情况下,您从旧值递减并递增新值。 这可能是一个新问题,但是...如何防止更新 candidate_id 列? 在触发器中引发异常,如果old.candiate_id is distinct from new.candidate_id

以上是关于在 PostgreSQL 中触发的主要内容,如果未能解决你的问题,请参考以下文章

postgresql中ROWTYPE、TYPE和RECORD的区别?

PostgreSQL 将数据库存储在哪里?

PostgreSQL 将数据库存储在哪里?

postgresql 安装

Pacemaker+Corosync搭建PostgreSQL集群

索引postgresql数组列以进行大于/小于比较?