概述:我试图在数据库中表示几种类型的实体,这些实体具有许多共同的基本字段,然后每个实体都有一些其他字段不与其他类型的实体共享。工作流程经常涉及将实体列在一起,所以我决定有一个包含其公共字段的表,然后每个实体都有自己的表及其附加字段。
实施:所有实体都有一个共同的领域,即“状态”;但是,某些实体仅支持所有可能状态的子集。我还希望每种类型的实体都强制使用其状态子集。最后,我还希望在将实体列在一起时包含此字段,因此将其从公共字段集中排除似乎不正确,因为这将需要特定类型表的并集以及SQL中缺少“implements interface”包含该字段将按惯例进行。
为什么我在这里:下面是一个功能正常的解决方案,但我感兴趣的是,有更好或更常见的方法来解决问题。特别是,这个解决方案要求我制作冗余unique
约束和冗余状态字段的事实感觉不够优雅。
create schema test;
create table test.statuses(
id integer primary key
);
create table test.entities(
id integer primary key,
status integer,
unique(id, status),
foreign key (status) references test.statuses(id)
);
create table test.statuses_subset1(
id integer primary key,
foreign key (id) references test.statuses(id)
);
create table test.entites_subtype(
id integer primary key,
status integer,
foreign key (id) references test.entities(id),
foreign key (status) references test.statuses_subset1(id),
foreign key (id, status) references test.entities(id, status) initially deferred
);
一些数据:
insert into test.statuses(id) values
(1),
(2),
(3);
insert into test.entities(id, status) values
(11, 1),
(13, 3);
insert into test.statuses_subset1(id) values
(1), (2);
insert into test.entites_subtype(id, status) values
(11, 1);
-- Test updating subtype first
update test.entites_subtype
set status = 2
where id = 11;
update test.entities
set status = 2
where id = 11;
-- Test updating base type first
update test.entities
set status = 1
where id = 11;
update test.entites_subtype
set status = 1
where id = 11;
/* -- This will fail
insert into test.entites_subtype(id, status) values
(12, 3);
*/
答案 0 :(得分:1)
MATCH SIMPLE
行为如果至少有一列具有默认MATCH SIMPLE
行为的多列外部约束为NULL
,则不会强制执行约束。您可以在此基础上进一步简化设计。
CREATE SCHEMA test;
CREATE TABLE test.status(
status_id integer PRIMARY KEY
,sub bool NOT NULL DEFAULT FALSE -- TRUE .. *can* be sub-status
,UNIQUE (sub, status_id)
);
CREATE TABLE test.entity(
entity_id integer PRIMARY KEY
,status_id integer REFERENCES test.status -- can reference all statuses
,sub bool -- see examples below
,additional_col1 text -- should be NULL for main entities
,additional_col2 text -- should be NULL for main entities
,FOREIGN KEY (sub, status_id) REFERENCES test.status(sub, status_id)
MATCH SIMPLE ON UPDATE CASCADE -- optionally enforce sub-status
);
非常便宜存储一些额外的NULL列(对于主要实体):
如果省略
refcolumn
列表,则会使用reftable
的主键。
演示数据:
INSERT INTO test.status VALUES
(1, TRUE)
, (2, TRUE)
, (3, FALSE); -- not valid for sub-entities
INSERT INTO test.entity(entity_id, status_id, sub) VALUES
(11, 1, TRUE) -- sub-entity (can be main, UPDATES to status.sub cascaded)
, (13, 3, FALSE) -- entity (cannot be sub, UPDATES to status.sub cascaded)
, (14, 2, NULL) -- entity (can be sub, UPDATES to status.sub NOT cascaded)
, (15, 3, NULL) -- entity (cannot be sub, UPDATES to status.sub NOT cascaded)
SQL Fiddle(包括您的测试)。
另一种选择是将(status_id, sub)
的所有组合输入status
表(每status_id
只能有2个)并且只有一个fk约束:
CREATE TABLE test.status(
status_id integer
,sub bool DEFAULT FALSE
,PRIMARY KEY (status_id, sub)
);
CREATE TABLE test.entity(
entity_id integer PRIMARY KEY
,status_id integer NOT NULL -- cannot be NULL in this case
,sub bool NOT NULL -- cannot be NULL in this case
,additional_col1 text
,additional_col2 text
,FOREIGN KEY (status_id, sub) REFERENCES test.status
MATCH SIMPLE ON UPDATE CASCADE -- optionally enforce sub-status
);
INSERT INTO test.status VALUES
(1, TRUE) -- can be sub ...
(1, FALSE) -- ... and main
, (2, TRUE)
, (2, FALSE)
, (3, FALSE); -- only main
等
相关答案:
如果由于某些原因需要所有四个表而不在问题中,请考虑对dba.SE上非常相似的问题的详细解决方案:
......可能是您描述的另一种选择。如果你能和some major limitations一起生活。相关回答: