如何正确设计数据库的这一部分(循环引用?)
How to properly design this part of a database (circular reference?)
情况:
A company has many projects
A project has many tags
A project belongs to only 1 company
A tag can belong to multiple projects
A company must have access to its own tags
示例 1:
在第一张图片中,公司的所有标签都可以通过 projects/project_tag 获得。但是,如果删除所有项目,则公司的标签将无法再访问,因为 project_tag 和项目之间的 link 消失了。
标签应该总是以某种方式 link 发送给公司,即使没有项目。
示例 2(标签也 linked 到公司):
在第二张图片中,它应该可以工作,但是现在是 'circular reference' ???
对于这样的问题,最好的解决方案应该是什么?
那么外键呢?
问题最后是:
如何针对这种情况正确设置 database/datamodel?
第二个例子中可能出错的例子:
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!
The last project_tag row is not valid because:
project 1 is linked to company_id 1
tag_id 2 is linked to 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
);
Tested:
- Rows inserted in project_tag are checked on the same company_id (else:
denied)
- Not possible to insert duplicate rows into project_tag
- If a project is removed, the linked project_tag rows are also removed
- If a tag is removed, the linked project_tag rows are also removed
- If a company is being removed while still having projects, the removal is rejected (see projects table: ON DELETE RESTRICT)
- If a company (without projects) is removed, all linked tags are removed also
首先,你的第二个模型完全正确,没有任何循环引用。
您应该将 Company
的 Company_ID
作为 F.K 传输到 Tags
和 Project
并制作它不为空。
然后,你应该将TAG_ID
和Project_ID
作为F.K传输到Project_Tag
中,并一起做唯一性。并且不需要将Project
和Tag
的Company_ID
(我们在上一段中传输的)传输到Project_Tag
。
现在,最后一个问题,您的最终要求:
THIS ROW IS NOT VALID!
您无法通过 ER 捕获它。您应该编写一些函数、触发器或存储过程来捕获和控制它。
编辑:
基于@reaanb 的评论和他的出色回答 :您可以通过这种方式控制此约束,但有一点冗余:
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),
);
情况:
A company has many projects
A project has many tags
A project belongs to only 1 company
A tag can belong to multiple projects
A company must have access to its own tags
示例 1:
在第一张图片中,公司的所有标签都可以通过 projects/project_tag 获得。但是,如果删除所有项目,则公司的标签将无法再访问,因为 project_tag 和项目之间的 link 消失了。 标签应该总是以某种方式 link 发送给公司,即使没有项目。
示例 2(标签也 linked 到公司):
在第二张图片中,它应该可以工作,但是现在是 'circular reference' ??? 对于这样的问题,最好的解决方案应该是什么? 那么外键呢?
问题最后是: 如何针对这种情况正确设置 database/datamodel?
第二个例子中可能出错的例子:
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!
The last project_tag row is not valid because:
project 1 is linked to company_id 1
tag_id 2 is linked to 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
);
Tested:
- Rows inserted in project_tag are checked on the same company_id (else: denied)
- Not possible to insert duplicate rows into project_tag
- If a project is removed, the linked project_tag rows are also removed
- If a tag is removed, the linked project_tag rows are also removed
- If a company is being removed while still having projects, the removal is rejected (see projects table: ON DELETE RESTRICT)
- If a company (without projects) is removed, all linked tags are removed also
首先,你的第二个模型完全正确,没有任何循环引用。
您应该将 Company
的 Company_ID
作为 F.K 传输到 Tags
和 Project
并制作它不为空。
然后,你应该将TAG_ID
和Project_ID
作为F.K传输到Project_Tag
中,并一起做唯一性。并且不需要将Project
和Tag
的Company_ID
(我们在上一段中传输的)传输到Project_Tag
。
现在,最后一个问题,您的最终要求:
THIS ROW IS NOT VALID!
您无法通过 ER 捕获它。您应该编写一些函数、触发器或存储过程来捕获和控制它。
编辑:
基于@reaanb 的评论和他的出色回答
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),
);