[英]PostgreSQL check constraint for foreign key condition
I have a table of users eg: 我有一个用户表,例如:
create table "user" (
id serial primary key,
name text not null,
superuser boolean not null default false
);
and a table with jobs: 和一张有工作的桌子:
create table job (
id serial primary key,
description text
);
the jobs can be assigned to users, but only for superusers. 可以将作业分配给用户,但只能分配给超级用户。 other users cannot have jobs assigned.
其他用户无法分配作业。
So I have a table whereby I see which job was assigned to which user: 因此,我有一个表格,可以查看将哪个作业分配给了哪个用户:
create table user_has_job (
user_id integer references "user"(id),
job_id integer references job(id),
constraint user_has_job_pk PRIMARY KEY (user_id, job_id)
);
But I want to create a check constraint that the user_id
references a user that has user.superuser = True
. 但是我想创建一个检查约束,以使
user_id
引用具有user.superuser = True
的用户。
Is that possible? 那可能吗? Or is there another solution?
还是有其他解决方案?
This would work for INSERTS: 这将适用于INSERTS:
create or replace function is_superuser(int) returns boolean as $$
select exists (
select 1
from "user"
where id = $1
and superuser = true
);
$$ language sql;
And then a check contraint on the user_has_job table: 然后在user_has_job表上检查约束:
create table user_has_job (
user_id integer references "user"(id),
job_id integer references job(id),
constraint user_has_job_pk PRIMARY KEY (user_id, job_id),
constraint chk_is_superuser check (is_superuser(user_id))
);
Works for inserts: 适用于插入物:
postgres=# insert into "user" (name,superuser) values ('name1',false);
INSERT 0 1
postgres=# insert into "user" (name,superuser) values ('name2',true);
INSERT 0 1
postgres=# insert into job (description) values ('test');
INSERT 0 1
postgres=# insert into user_has_job (user_id,job_id) values (1,1);
ERROR: new row for relation "user_has_job" violates check constraint "chk_is_superuser"
DETAIL: Failing row contains (1, 1).
postgres=# insert into user_has_job (user_id,job_id) values (2,1);
INSERT 0 1
However this is possible: 但是,这是可能的:
postgres=# update "user" set superuser=false;
UPDATE 2
So if you allow updating users you need to create an update trigger on the users table to prevent that if the user has jobs. 因此,如果允许更新用户,则需要在users表上创建一个更新触发器,以防止该用户有工作。
The only way I can think of is to add a unique constraint on (id, superuser)
to the users
table and reference that from the user_has_job
table by "duplicating" the superuser
flag there: 我能想到的唯一方法是在
(id, superuser)
上向users
表添加唯一约束(id, superuser)
并通过“复制” superuser
标志从user_has_job
表中引用该user_has_job
:
create table users (
id serial primary key,
name text not null,
superuser boolean not null default false
);
-- as id is already unique there is no harm adding this additional
-- unique constraint (from a business perspective)
alter table users add constraint uc_users unique (id, superuser);
create table job (
id serial primary key,
description text
);
create table user_has_job (
user_id integer references users (id),
-- we need a column in order to be able to reference the unique constraint in users
-- the check constraint ensures we only reference superuser
superuser boolean not null default true check (superuser),
job_id integer references job(id),
constraint user_has_job_pk PRIMARY KEY (user_id, job_id),
foreign key (user_id, superuser) references users (id, superuser)
);
insert into users
(id, name, superuser)
values
(1, 'arthur', false),
(2, 'ford', true);
insert into job
(id, description)
values
(1, 'foo'),
(2, 'bar');
Due to the default
value, you don't have to specify the superuser
column when inserting into the user_has_job
table. 由于使用
default
值,因此在插入到user_has_job
表中时不必指定superuser
列。 So the following insert works: 因此,以下插入有效:
insert into user_has_job
(user_id, job_id)
values
(2, 1);
But trying to insert arthur into the table fails: 但是尝试将亚瑟插入表中失败:
insert into user_has_job
(user_id, job_id)
values
(1, 1);
This also prevents turning ford into a non-superuser. 这也可以防止将福特变成非超级用户。 The following update:
以下更新:
update users
set superuser = false
where id = 2;
fails with the error 失败并显示错误
ERROR: update or delete on table "users" violates foreign key constraint "user_has_job_user_id_fkey1" on table "user_has_job"
错误:表“ users”上的更新或删除违反了表“ user_has_job”上的外键约束“ user_has_job_user_id_fkey1”
Detail: Key (id, superuser)=(2, t) is still referenced from table "user_has_job".详细信息:仍从表“ user_has_job”中引用键(id,superuser)=(2,t)。
Create a separate superuser
table that inherits from the user
table: 创建一个从
user
表继承的单独的superuser
user
表:
CREATE TABLE "user" (
id serial PRIMARY KEY,
name text NOT NULL,
);
CREATE TABLE superuser () INHERITS ("user");
The user_has_job
table can then reference the superuser
table: 然后,
user_has_job
表可以引用superuser
表:
CREATE TABLE user_has_job (
user_id integer REFERENCES superuser (id),
job_id integer REFERENCES job(id),
PRIMARY KEY (user_id, job_id)
);
Move users around between the tables as needed by inserting and deleting: 通过插入和删除,根据需要在表之间移动用户:
WITH promoted_user AS (
DELETE FROM "user" WHERE id = 1 RETURNING *
) INSERT INTO superuser (id, name) SELECT id, name FROM promoted_user;
I don't know if this is a good way to do it but it seems to work 我不知道这是否是一个好方法,但似乎可行
INSERT INTO user_has_job (user_id, job_id) VALUES (you_user_id, your_job_id)
WHERE EXIST (
SELECT * FROM user WHERE id=your_user_id AND superuser=true
);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.