我应该如何建立表之间的关系?

How should I build the relationships between my tables?

我正在将发票组件构建到我的 Web 应用程序中。 在我的架构中,我们有某些客户可能想要开具发票的级别。

Projects -> Sites -> Jobs -> Phases -> Teams

它们中的每一个都在数据库中 tables 并且与它们的父级有关系。

我计划构建一个 Items table 来保存关于我们将如何为在每个级别完成的工作开具发票的一般信息,即姓名、默认价格、计量单位等

然后用户将创建一个 发票模型,他们可以将 项目 列表关联到该模型,以便这些可以重复使用。

这就是我的问题发挥作用的地方。当需要开具发票时,我需要将 发票模型 与上述级别之一相关联。我不想为发票模型的每个级别创建 linking table。我想以某种方式保持 table 之间的关系完整性。而且,如果将来还有其他我可以计费的东西,我不想为了考虑这个新的 "Billable Level" 而必须进行巨大的数据库更改 and/or 代码更改。

有没有办法让我在不创建新的 table 到 link 发票模型的情况下保持关系完整性?

为了清楚地回答您的问题,您必须至少创建另外两个表才能保持关系完整性,一个用于存储您的模型和项目之间的关系 ID,另一个用于存储您想要的项目的所有数据创建,在这种情况下,您最终只会多出两个表,但未来的痛苦将得到治愈。

可以实现多态关系,解决附加 "n" 个不同实体类型的问题。

据我所知,多态关系的缺点是,在不损失参照完整性的情况下实现它是不可能的。

https://hashrocket.com/blog/posts/modeling-polymorphic-associations-in-a-relational-database 对此有一个很好的了解。一种流行且最简单的实现是多态连接,引用自以下文章:

Polymorphic Joins

A simple approach to connect an acl to a resource is to use two columns on the acl table: resource_type and resource_id. This approach was popularized by Ruby on Rails. The acl table could be defined as follows:

create table acl(
  id serial primary key,
  resource_type varchar not null,
  resource_id integer not null,
  -- other fields omitted
  unique(resource_id, resource_type)
);

The query for retrieving an acl for document id:42 would be like the following:

select *
from acl
where resource_type='document'
  and resource_id=42;

A serious problem with this approach is the database is very limited in the data integrity it can enforce due to the lack of foreign key constraints. It can ensure a resource has no more that one acl, but that is all. A resource can be missing an acl and an acl can point to a missing resource.


Exclusive Belongs To 方法是提高参照完整性的好方法,但需要为每种可能的实体类型创建一个新列。引用文章:

Exclusive Belongs To (AKA Exclusive Arc) In this model, the acl has foreign keys to all tables to which it can belong.

create table acl(
  id serial primary key,
  document_id integer references document,
  image_id integer references image,
  file_id integer references file,
  report_id integer references report,
  -- other fields omitted
  check(
    (
      (document_id is not null)::integer +
      (image_id is not null)::integer +
      (file_id is not null)::integer +
      (report_id is not null)::integer 
    ) = 1
  )
);

create unique index on acl (document_id) where document_id is not null;
create unique index on acl (image_id) where image_id is not null;
create unique index on acl (file_id) where file_id is not null;
create unique index on acl (report_id) where report_id is not null;

Take note of the check constraint. This ensures that an acl belongs to exactly one resource of any type. With this design an acl cannot be orphaned, but there is no way to enforce that a resource has an acl. Also important are the partial unique indexes. Limiting the unique indexes to only not null values dramatically saves space as well as reducing write operations on insert.