我想知道在事务即将提交之前是否可以间接执行触发器?在此触发器中,我将进行一致性检查并在需要时回滚事务。
例如,我有三个表:
users (id, name)
groups (id, name)
user_in_group (user_id, group_id)
我想创建一个触发器,用于验证用户是否始终是组的一部分。不允许孤儿用户。每次插入用户时,此触发器将验证是否还发生了进入user_in_group的相应插入。如果没有,交易将不会提交。
使用简单的基于行或语句的触发器无法完成此操作,因为上述方案需要两个单独的语句。
另一种方法是,当发生从user_in_group的删除时,可以通过基于行的触发器轻松完成。
答案 0 :(得分:18)
您是否使用DEFERRABLE (INITIALLY DEFERRED)
选项查看了CREATE CONSTRAINT TRIGGER?
答案 1 :(得分:2)
真正的方法不是真的要在数据库中建立约束吗?这似乎正是约束的目的。添加一个外键约束和一个非null,看起来你应该在业务中。
现在针对对称约束进行了修订:
drop table foousers cascade;
drop table foogroups cascade;
drop table foousergrps cascade;
create table foousers (id int primary key, name text);
create table foogroups (id int primary key, name text);
create table foousergrps (user_id int unique references foousers not null, group_id int unique references foogroups not null);
alter table foogroups add foreign key (id) references foousergrps (group_id) deferrable initially deferred;
alter table foousers add foreign key (id) references foousergrps (user_id) deferrable initially deferred;
begin;
insert into foousers values (0, 'root');
insert into foousers values (1, 'daemon');
insert into foogroups values (0, 'wheel');
insert into foogroups values (1, 'daemon');
insert into foousergrps values (0,0);
insert into foousergrps values (1,1);
commit;
禁止:
insert into foousers values (2, 'bad');
insert into foousergrps values (2,2);
(不可延迟,嘘)检查功能的例子:
create table foousergrps (user_id int unique references foousers not null, group_id int not null);
create function fooorphangroupcheck(int) returns boolean as $$
declare
gid alias for $1;
begin
perform 1 from foousergrps where group_id = gid limit 1;
if NOT FOUND then return false;
end if;
return true;
end;
$$
LANGUAGE 'plpgsql';
alter table foogroups add check (fooorphangroupcheck(id));
答案 2 :(得分:1)
看看文档,似乎没有这样的触发选项......所以实现“无孤儿用户”规则的一种方法是不允许直接插入users
和user_in_group
表。而是创建一个视图(将这些表组合,即user_id, user_name, group_id
)与update rule,将数据插入到右表中。
或者只允许通过存储过程插入新用户,该存储过程将所有必需数据作为inpud,因此不允许用户使用组。
顺便说一句,为什么你有单独的用户和组关系表?为什么不将group_id
字段添加到具有users
约束的FK / NOT NULL
表中?
答案 3 :(得分:0)
来自the docs。 。
触发器可以定义为在任何INSERT之前或之后执行, 更新或删除操作,每个修改行一次,或每次一次 SQL语句。
答案 4 :(得分:0)
您可以使用sql WITH
运算符,如下所示:
WITH insert_user AS (
INSERT INTO users(name) VALUES ('bla-bla-user') RETURNING id
)
INSERT INTO user_in_group(user_id, group_id)
SELECT id, 999 FROM insert_user UNION
SELECT id, 888 FROM insert_user;
SELECT groups (id, name) user_in_group (user_id, group_id)