如何正确设计数据库的这一部分(循环引用?)

时间:2018-01-05 08:54:01

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

情况:

  

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

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

  公司必须能够访问自己的标签

示例1:

enter image description here

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

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

enter image description here

在第二张图片中,它应该有效,但现在这是一个循环参考' ??? 对于像这样的问题,什么应该是最好的解决方案? 外键怎么样?

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

第二个例子中出现问题的示例:

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行无效,因为:
  项目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
);
  

测试:
   - 在相同的company_id 中检查插入project_tag中的行(否则:    否认)
   - 无法将重复行插入project_tag
   - 如果删除项目,则也会删除链接的 project_tag行    - 如果删除了代码,则也会删除已链接的 project_tag行    - 如果在仍有项目的情况下拆除公司,则拒绝拆除(参见项目表:ON DELETE RESTRICT)
   - 如果删除了公司(没有项目),则所有链接的标签也将被删除

1 个答案:

答案 0 :(得分:1)

首先,你的第二个模型绝对正确,并且没有任何循环引用。

您应该将Company_ID Company作为 FK 传送到TagsProject并将其设为非空

然后,您应将TAG_IDProject_ID作为 F.K 传输到Project_Tag并将其组合在一起。并且无需将Company_ID ProjectTag(我们在上一段中传输)传输到Project_Tag

现在,最后的问题,你的最终要求如何:

  

这条行没有用!

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

修改
基于@ reaanb的评论和他的好答案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),
);