在Postgres中强制执行外键的类型相等性

时间:2018-12-04 20:16:32

标签: postgresql constraints

我有一个具有多种实体类型的实体表和一个实体关系表,该表需要强制其只包含相同类型的实体之间的关系。

目前,我有两种解决方法:

  1. CREATE TABLE entity (
      id uuid PRIMARY KEY,
      type my_enum_type NOT NULL,
      -- … more 
    );
    
    CREATE TABLE relation (
      id uuid PRIMARY KEY,
      x uuid REFERENCES entity NOT NULL,
      y uuid REFERENCES entity NOT NULL,
      CHECK(entity(x).type = entity(y).type)
      -- Doesn't work because CHECK cannot reference other tables.
    );
    
  2. 幸运的是,我目前只有两种类型,并且不希望很快改变。但是继承不能与外键很好地混合,因此它变得非常冗长:

    CREATE TABLE entity (
      id uuid PRIMARY KEY,
      -- … more fields
    );
    CREATE TABLE entity_a (
      PRIMARY KEY (id)
    ) INHERITS (entity);
    CREATE TABLE entity_b (
      PRIMARY KEY (id)
    ) INHERITS (entity);
    
    CREATE TABLE relation (
      id uuid PRIMARY KEY,
      x uuid NOT NULL,
      y uuid NOT NULL,
      -- … more fields
    );
    CREATE TABLE relation_a (
      PRIMARY KEY (id),
      FOREIGN KEY (x) REFERENCES entity_a (id),
      FOREIGN KEY (y) REFERENCES entity_a (id)
    ) INHERITS (relation);
    CREATE TABLE relation_b (
      PRIMARY KEY (id),
      FOREIGN KEY (x) REFERENCES entity_b (id),
      FOREIGN KEY (y) REFERENCES entity_b (id)
    ) INHERITS (relation);
    

第二种方法肯定具有它可以工作的优点,但是它是冗长的,不可扩展的,并且与完全独立的表定义相比,唯一的优点是它避免了复制所有其他字段(然后可能忘记了在两个地方都进行更新)

关于如何更优雅地解决此问题的任何建议?

1 个答案:

答案 0 :(得分:1)

以下语句将为您的解决方案1)添加约束,以便始终满足条件:

/* we need a (redundant) UNIQUE constraint as target for foreign keys */
ALTER TABLE entity ADD UNIQUE (type, id);

/* add a (redundant) "type" column and fill it from "entity" */
ALTER TABLE relation ADD type my_enum_type;

UPDATE relation SET type = e.type
   FROM entity AS e
   WHERE relation.x = e.id;

ALTER TABLE relation ALTER type SET NOT NULL;

/* now we can add foreign keys that guarantee your condition */
ALTER TABLE relation ADD FOREIGN KEY (type, x) REFERENCES entity (type, id);
ALTER TABLE relation ADD FOREIGN KEY (type, y) REFERENCES entity (type, id);

/* remove the bloat (optional) */
VACUUM (FULL) relation;

是的,它添加了一个多余的列和一个多余的约束,但是我认为这是确保您的状况最优雅自然的方法。