在 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的区别?