2012-04-13 21 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