如何在Postgres中建模多表"继承"?

Ian*_*lor 6 sql database postgresql schema

我正在尝试在Postgres数据库中建模一些需要"继承"的不同用例,并意识到有多种方法可以做到这一点,我不确定哪种是最好的.

我想到的两个具体用例是:

  • "成员资格" - 单个"用户"可以是不同类型对象的成员:"团队","集合"或"项目".在这种情况下,成员类型的上限是这三种关系类型.(目前.)

  • "事件" - 可以创建与我的数据模型中的任何其他对象相关的"事件" - 例如."teams.create","users.update","webhooks.create","items.publish"等.在这种情况下,关系的"类型"并没有真正限制,因为每当我添加新的数据模型时,我很有可能想要为它创建事件.

以下是我可以看到实现这些需求的几种方法......

(对这些代码片段使用"Memberships"用例,但同样的事情也适用于"Events"用例.)


1. XOR列

CREATE TABLE memberships (
  id TEXT,
  user_id TEXT,
  team_id TEXT,
  collection_id TEXT,
  item_id TEXT,
  PRIMARY KEY (id),
  FOREIGN KEY (user_id) REFERENCES users (id),
  FOREIGN KEY (team_id) REFERENCES teams (id),
  FOREIGN KEY (collection_id) REFERENCES collections (id),
  FOREIGN KEY (item_id) REFERENCES items (id),
  CHECK (
    (team_id IS NOT NULL)::integer + 
    (collection_id IS NOT NULL)::integer +
    (item_id IS NOT NULL)::integer = 1
  )
);
Run Code Online (Sandbox Code Playgroud)

优点:

  • 数据库为您处理外来关系.
  • JOINs不那么冗长.

缺点:

  • NULL每行都有额外的列值.
  • CHECK约束是有点不可思议?
  • 新的"类型"需要添加一*_id列并更改CHECK.

2.一对列

CREATE TABLE memberships (
  id TEXT,
  user_id TEXT,
  target_type TEXT,
  target_id TEXT,
  PRIMARY KEY (id),
  FOREIGN KEY (user_id) REFERENCES users (id),
  CHECK (target_type IN ('team', 'collection', 'items'))
);
Run Code Online (Sandbox Code Playgroud)

优点:

  • 添加新"类型"不需要添加列.
  • 每行没有额外的NULL列值.
  • 您不需要约束来确保每行只存在一个目标.

缺点:

  • 您必须保证应用程序层中的数据一致性.
  • 编写JOIN查询有点冗长.
  • CHECK添加新"类型"时必须更改约束.

3.许多表格

CREATE TABLE team_memberships (
  id TEXT,
  user_id TEXT,
  team_id TEXT,
  PRIMARY KEY (id),
  FOREIGN KEY (user_id) REFERENCES users (id),
  FOREIGN KEY (team_id) REFERENCES teams (id)
);

CREATE TABLE collection_memberships (
  id TEXT,
  user_id TEXT,
  collection_id TEXT,
  PRIMARY KEY (id),
  FOREIGN KEY (user_id) REFERENCES users (id),
  FOREIGN KEY (collection_id) REFERENCES collections (id)
);

CREATE TABLE item_memberships (
  id TEXT,
  user_id TEXT,
  item_id TEXT,
  PRIMARY KEY (id),
  FOREIGN KEY (user_id) REFERENCES users (id),
  FOREIGN KEY (item_id) REFERENCES items (id)
);
Run Code Online (Sandbox Code Playgroud)

优点:

  • 数据库为您处理外键关系.
  • NULL任何行中都没有额外的列值.

缺点:

  • 您最终会创建大量表,每种关系类型一个.

栏目对方法似乎是普遍存在的,但它意味着数据库不再会自动为你办理外键关系,这似乎是一个相当大的缺点,之类的东西CASCADE不再是一个选项.对我来说,这似乎是一个负面因素,使它成为一个合法的选择.

多个表的方法也似乎是普遍存在的,而对于"成员"使用情况下,它是有道理的.但对于其他用例,例如"事件"(可以创建与数据库中的任何对象相关的事件,而不是预约束列表),那么拥有如此大量的表似乎不实用.

XOR列的方法似乎并不普遍,或者至少我无法找到关于它的尽可能多的信息,但感觉像一个很好的平衡,仍然可以让数据库完成所有的工作,而无需添加大量的表.

XOR Columns方法有任何明显的问题吗?

有没有推荐的解决方法?