2

概述:我试图在数据库中表示几种类型的实体,它们有许多共同的基本字段,然后每个都有一些不与其他类型的实体共享的附加字段。工作流程经常涉及将实体列在一起,所以我决定有一个包含公共字段的表,然后每个实体都有自己的表和附加字段。

实施:所有实体都有一个公共字段“状态”;但是,某些实体仅支持所有可能状态的子集。我还希望每种类型的实体都强制使用其状态子集。最后,我还希望在将实体一起列出时包含该字段,因此将其从公共字段集中排除似乎是不正确的,因为这需要特定类型表的联合,并且 SQL 中缺少“实现接口”意味着列入该领域将是惯例。

为什么我在这里:下面是一个功能性的解决方案,但如果有更好或更常见的方法来解决问题,我很感兴趣。特别是,这个解决方案需要我做一个冗余的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);
*/
4

1 回答 1

2

简化基于MATCH SIMPLEfk 约束行为的构建

如果至少一列具有默认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(包括您的测试)。

单 FK 的替代方案

另一种选择是将 的所有组合输入(status_id, sub)status表中(只能有 2 个 per status_id)并且只有一个 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 上非常相似的问题的详细解决方案:

遗产

...可能是您所描述的另一种选择。如果你能忍受一些重大的限制。相关答案:

于 2014-07-23T15:32:38.297 回答