受限于其裁判的子集的关系表

时间:2017-03-02 17:50:37

标签: sql sqlite foreign-keys subset

说我有下表:

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;

......但这看起来有点严重!

2 个答案:

答案 0 :(得分:1)

documentation说:

  

通常,外键约束的父键是父表的主键。如果它们不是主键,则父键列必须共同受UNIQUE约束或具有UNIQUE索引。

缺少UNIQUE约束导致&#34;外键不匹配&#34;错误。

在CHECK约束中不可能使用子查询,因此强制执行mode = 1约束的唯一方法是使其成为外键约束的一部分,或者使用触发器。

答案 1 :(得分: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,N = 5

的SQL Server示例
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')