2012-04-13 85 views
4

假设我有以下各表如何使一个外键与PostgreSQL中对被引用表的约束

CREATE TABLE plugins (
id int primary key, 
type text); 

insert into plugins values (1,'matrix'); 
insert into plugins values (2,'matrix'); 
insert into plugins values (3,'function'); 
insert into plugins values (4,'function'); 

CREATE TABLE matrix_params (
id int primary key, 
pluginid int references plugins (id) 
); 

这是预期,但我想补充一个附加的约束所有的作品一个matrix_param只能请参阅具有类型'矩阵'的pluginid。所以

insert into matrix_params values (1,1); 

应该会成功,但

insert into matrix_params values (2,3); 

应该失败。

对于matrix_params的简单约束不起作用,因为它无法知道相应类型在插件表中。

回答

3

您可以对此使用CHECK约束。您不能将查询放在CHECK约束中,但可以调用一个函数;所以,我们建立了一个简单的函数,它告诉我们,如果一个pluginid是一个矩阵:

create or replace function is_matrix(int) returns boolean as $$ 
    select exists (
     select 1 
     from plugins 
     where id = $1 
      and type = 'matrix' 
    ); 
$$ language sql; 

,敷在CHECK约束:

alter table matrix_params add constraint chk_is_matrix check (is_matrix(pluginid)); 

然后:

=> insert into matrix_params values (1,1); 
=> insert into matrix_params values (2,3); 
ERROR: new row for relation "matrix_params" violates check constraint "chk_is_matrix" 

而FK负责参照完整性和级联。

+1

如果matrix_params行的引用的插件一行之后型更新,事情会不会在期望的状态;否则这个工程。 – kgrittn 2012-04-13 12:31:22

+0

@kgrittn:你可以在'plugins'上添加一个UPDATE触发器(如果你的PostgreSQL版本支持它,可能用'WHEN old.type ='矩阵'和new.type!= old.type'条件),那个触发器可能会引发如果更新违反了'is_matrix'条件,则会发生异常。虽然这可能会变得有点难看。 – 2012-04-13 18:36:24

2

在引用表中使用复合键,并在引用表中使用约束CHECK

CREATE TABLE plugins (
id int primary key, 
type text, 
UNIQUE (type, id) 
); 

CREATE TABLE matrix_params (
id int primary key, 
plugintype text DEFAULT 'matrix' NOT NULL 
    CHECK (plugintype = 'matrix'), 
pluginid int NOT NULL, 
FOREIGN KEY (plugintype, pluginid) 
    references plugins (type, id) 
); 
+0

这会将一个表的主键更改为问题中指定的内容之外的内容,并向另一个表中的每一行添加不必要的列。 – kgrittn 2012-04-13 12:33:46

+0

@kgrittn:我没有改变任何主键:我已经为引用的表添加了一个超级键。引用表中添加的列允许使用行级别的CHECK约束(使用外键),因此在上下文中它必须是**。但如果它困扰你,它可以通过视图“隐藏”。 – onedaywhen 2012-04-13 13:00:33

+0

我认为这是一个有效的解决方案。当你想到它通过plugintype时,pluginid并不是真正的关键,因为(1,'foo'),(1,'bar)不应该被允许在插件表中。在我的情况下,我试图使用常量作为key的一部分,postgres不喜欢它。我更喜欢选择的答案,因为它完成了工作,而无需修改数据类型。 – hsikcah 2012-04-13 14:45:09

0

处理此问题的一种方法是使用可序列化事务。

http://wiki.postgresql.org/wiki/SSI#FK-Like_Constraints

+0

那么,如果没有一些非规范化(在matrix_params中冗余存储数据)或者声明为IMMUTABLE是一个非常稳定的函数,这会带来一定的风险,那么这不是一个外键。 (当你对数据库说谎时,它迟早会和你一起。)所以唯一真正准确的答案就是它不能完成。但这不是很有帮助。这是一种非常常见的用例,正是可序列化事务旨在解决的事情。也许我的链接应该在这里:http://wiki.postgresql.org/wiki/SSI#FK-Like_Constraints – kgrittn 2012-04-13 14:53:46

+0

这使得更多的意义,谢谢澄清:) – onedaywhen 2012-04-13 14:57:29

+0

好吧,我编辑答案使用更具体链接。 – kgrittn 2012-04-13 15:34:55