【问题标题】:How to properly design this part of a database (circular reference?)如何正确设计数据库的这一部分(循环引用?)
【发布时间】:2023-03-23 03:42:01
【问题描述】:

情况:

一家公司有很多项目
一个项目有很多标签

一个项目只属于一家公司
一个标签可以属于多个项目

公司必须有权访问自己的标签

示例 1:

在第一张图片中,公司的所有标签都可以通过 projects/project_tag 获得。但是如果所有项目都被删除,那么公司的标签将无法再访问,因为 project_tag 和项目之间的链接已经消失。 标签应该总是与公司相关联,即使没有项目。

示例 2(其中标签也与公司相关联):

在第二张图片中,它应该可以工作,但这现在是“循环引用”吗? 对于这样的问题,最好的解决方案应该是什么? 那么外键呢?

问题最后是: 如何针对这种情况正确设置数据库/数据模型?


第二个例子中可能出错的例子:

companies:
id=1, name=MyCompany
id=2, name=OtherCompany

tags:
id=1, company_id=1, name=MyTag
id=2, company_id=2, name=OtherTag

projects:
id=1, company_id=1, name=MyProject

project_tag:
project_id=1, tag_id=1
project_id=1, tag_id=2 --> THIS ROW IS NOT VALID!

最后一个 project_tag 行无效,因为:
project 1 已链接到 company_id 1
tag_id 2 已链接到 company_id 2


更新:感谢大家提供的信息!

根据接受的答案,PostgreSQL 的 CREATE 查询将变为:

CREATE TABLE companies (
   id SERIAL PRIMARY KEY NOT NULL,
   name TEXT NOT NULL
);
CREATE TABLE projects (
   id SERIAL PRIMARY KEY NOT NULL,
   company_id INT NOT NULL,
   name TEXT NOT NULL,
   UNIQUE (id, company_id),
   FOREIGN KEY (company_id) REFERENCES companies (id) ON DELETE RESTRICT ON UPDATE CASCADE
);
CREATE TABLE tags (
   id SERIAL PRIMARY KEY NOT NULL,
   company_id INT NOT NULL,
   name TEXT NOT NULL,
   UNIQUE (id, company_id),
   FOREIGN KEY (company_id) REFERENCES companies (id) ON DELETE CASCADE ON UPDATE CASCADE
);
CREATE TABLE project_tag (
   id SERIAL PRIMARY KEY NOT NULL,
   company_id INT NOT NULL,
   project_id INT NOT NULL,
   tag_id INT NOT NULL,
   UNIQUE (company_id, project_id, tag_id),
   FOREIGN KEY (company_id, project_id) REFERENCES projects (company_id, id) ON DELETE CASCADE ON UPDATE CASCADE,
   FOREIGN KEY (company_id, tag_id) REFERENCES tags (company_id, id) ON DELETE CASCADE ON UPDATE CASCADE
);

已测试:
- 在 same company_id 上检查插入 project_tag 的行(否则: 拒绝)
- 无法将重复行插入到 project_tag
- 如果项目被删除,则链接的 project_tag 行也会被删除
- 如果标签被移除,则链接的 project_tag 行 也会被移除
- 如果公司在仍有项目的情况下被移除,则移除被拒绝(参见项目表:ON DELETE RESTRICT)
- 如果一个公司(没有项目)被删除,所有链接的标签也会被删除

【问题讨论】:

  • “循环引用”没有错。请解释你认为一个是什么以及为什么你认为他们有问题。你的问题是基于误解。此外,基数不足以进行设计。您必须给出感兴趣的关系(船)/关联的含义——每个都由一个表/关系表示。 (“有”、“属于”和“链接到”是无可救药的笼统。)然后考虑到可能出现的情况,找到约束——基数反映了其中的一些。阅读信息建模和关系数据库设计简介,包括对更高 NF 的规范化。

标签: database database-design relational-database circular-reference datamodel


【解决方案1】:

首先,您的第二个模型是绝对正确的,其中没有任何循环引用。

您应该将Company_IDCompany 作为F.K 传输到TagsProject 并使其Not Null

然后,您应该将TAG_IDProject_ID 作为F.Ks 传输到Project_Tag 并一起制作唯一的。并且无需将ProjectTag(我们在上一段中传输)的Company_ID 传输到Project_Tag

现在,最后一个问题如何,您的最终要求:

此行无效!

无法通过 ER 捕获它。您应该编写一些函数、触发器或存储过程来捕获和控制它。

编辑
基于@reaanb 的 cmets 和他的出色回答 here:您可以通过这种方式控制此约束,但有一点冗余:

CREATE TABLE Project(
    project_id INT NOT NULL,
    company_id INT NOT NULL,
    PRIMARY KEY (project_id),
    FOREIGN KEY (company_id) REFERENCES Company (id),
    UNIQUE KEY (project_id, company_id)
);

CREATE TABLE Tag(
    tag_id INT NOT NULL,
    company_id INT NOT NULL,
    PRIMARY KEY (tag_id),
    FOREIGN KEY (company_id) REFERENCES Company (id),
    UNIQUE KEY (tag_id, company_id)
);

CREATE TABLE Project_Tags(
    id INT NOT NULL,
    company_id INT NOT NULL,
    project_id INT NOT NULL,
    tag_id INT NOT NULL,

    PRIMARY KEY (id),
    UNIQUE KEY (tag_id, project_id)

    FOREIGN KEY (project_id, company_id) REFERENCES Project (project_id, company_id),
    FOREIGN KEY (tag_id, company_id) REFERENCES Tag (tag_id, company_id),
);

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2014-01-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-22
  • 1970-01-01
相关资源
最近更新 更多