2017-03-02 25 views
0

說我下面的表格有:關係表

create table foo (
    id integer primary key, 
    mode integer not null check (mode in (1, 2)) 
); 

現在我想存儲有關從該表中記錄更多的信息,但只有那些有,比方說,mode = 1 。我想我可以做到這一點的方法如下:

create table foo_bar (
    id  integer primary key, 
    foo_id integer, 
    _mode integer not null default (1) check (_mode = 1), 

    foreign key (foo_id, _mode) references foo(id, mode) 
); 

也就是說,我們在foo_bar僞列,它是強制始終等於1,包括外鍵約束與foo

但是,這不僅不起作用(使用pragma foreign_keys = ON;),但是當您向foo插入多個值時,可能會違反外鍵約束!

sqlite> insert into foo(mode) values (1); 
sqlite> insert into foo(mode) values (2); 
sqlite> select * from foo; 
1|1 
2|2 
sqlite> insert into foo_bar(foo_id) values (1); 
Error: foreign key mismatch - "foo_bar" referencing "foo" 
sqlite> insert into foo_bar(foo_id) values (2); 
Error: foreign key mismatch - "foo_bar" referencing "foo" 
sqlite> insert into foo(mode) values (1), (2); 
Error: foreign key mismatch - "foo_bar" referencing "foo" 

這是SQLite中的錯誤(我使用3.17,它很重要),還是我做錯了?

另外一種方式,沒有外鍵,可能是使用觸發器:

create trigger bad_mode 
before insert on foo_bar when (select mode from foo where id = NEW.foo_id) <> 1 
begin 
    select raise(fail, "Invalid mode"); 
end; 

...但是這似乎有點噁心了!

回答

1

documentation說:

通常,外鍵約束的母密鑰是的主鍵父表。如果它們不是主鍵,那麼父鍵列必須共同受到UNIQUE約束或具有UNIQUE索引。

缺少唯一約束是導致「外鍵不匹配」錯誤的原因。


這是不可能在CHECK約束使用子查詢,所以強制執行mode = 1約束的唯一途徑是讓外鍵約束的一部分,或使用觸發器。

+0

有用的知道......儘管如此,我仍然試圖將問題正常化!謝謝 :) – Xophmeister

0

我想你可以改進設計,並不會比FK其他約束需要:

  • 所有foo.mode首先可能是一個外鍵到另一個表,說modes,無需使用支票約束。您將在那裏存儲值1和2。

  • 第二個表格bar_modes可能有一個字段mode,FK爲modes。您將在此表中存儲值1。

  • 最後,foo_bar.foo_id將是FK到foo.idfoo_bar.mode從FK到bar_modes.mode

讓我知道您的想法!對於M = 3

SQL Server的實例中,N = 5

create table modes (
    id int primary key 
) 

create table foo (
    id int primary key, 
    mode int not null foreign key references modes(id) 
); 

create table bar_modes (
    id int primary key foreign key references modes(id) 
) 

create table foo_bar (
    id int primary key, 
    foo_id int foreign key references foo(id), 
    other_data varchar(20) 
) 

insert modes (id) values(1) 
insert modes (id) values(2) 
insert modes (id) values(3) 
insert modes (id) values(4) 
insert modes (id) values(5) 

insert bar_modes (id) values (1) 
insert bar_modes (id) values (2) 
insert bar_modes (id) values (3) 

insert foo (id, mode) values (1000, 1) 
insert foo (id, mode) values (2000, 2) 
insert foo (id, mode) values (2500, 2) 
insert foo (id, mode) values (5000, 5) 

insert foo_bar (id, foo_id, other_data) values (100, 1000, 'data for foo 1000') 
insert foo_bar (id, foo_id, other_data) values (200, 2000, 'data for foo 2000') 
insert foo_bar (id, foo_id, other_data) values (250, 2500, 'data for foo 2500') 
+0

謝謝。 「foo.mode」的檢查只是爲了說明我的觀點;在我的實際模式中,我確實有一個我參考的「模式」表,正如你所建議的那樣。無論如何,我認爲一個不同的模式可能是要走的路,但不是你如何建議,我想我會把模式關係分解成一個新表。這適用於我的用例,我只對子集中的一種模式感興趣,但它不是一個通用的解決方案。 – Xophmeister

+0

什麼是一般問題,您正在尋找一個通用的解決方案? –

+0

我不是在尋找一個通用的解決方案,而是說例如你有N個模式,'foo_bar'只允許來自M模式(其中M <= N)的'foo'的記錄。對於M = 1(我的情況),你可以通過一個新表來實現這一點,該表將foo的記錄與模式連接,然後將FK連接到foo.id(但是在模式中會丟失任何隱含的語義) ;如果M = N,那麼它會變得更加微不足道,因爲您只需將FK添加到原始表中的ID和模式(不需要額外的表格)。但是,當1 Xophmeister