我在我的应用中有以下“评论”表:
comments
--------
id INT
foreign_id INT
model TEXT
comment_text TEXT
...
这个表的想法是存储我的应用程序的各个部分的注释 - 它可以存储博客帖子的评论,即:
1|34|blogpost|lorem ipsum...
用户图片:
2|12|picture|lorem ipsum...
等等。
现在,有没有办法强制对这些数据进行FOREIGN KEY约束?
即。在评论表中这样的东西:
FOREIGN KEY (`foreign_id`) REFERENCES blogposts (`id`)
//but only when model='blogpost'
答案 0 :(得分:85)
您正在尝试进行名为 Polymorphic Associations 的设计。也就是说,外键可以引用几个相关表中的任何一行。
但是外键约束必须恰好引用一个表。您不能声明引用不同表的外键,具体取决于Comments
表的另一列中的值。这会违反关系数据库设计的几个规则。
更好的解决方案是制作一种由评论引用的“超级”。
CREATE TABLE Commentable (
id SERIAL PRIMARY KEY
);
CREATE TABLE Comments (
comment_id SERIAL PRIMARY KEY,
foreign_id INT NOT NULL,
...
FOREIGN KEY (foreign_id) REFERENCES Commentable(id)
);
您的每种内容类型都将被视为此超类型的子类型。这类似于接口的面向对象概念。
CREATE TABLE BlogPosts (
blogpost_id INT PRIMARY KEY, -- notice this is not auto-generated
...
FOREIGN KEY (blogpost_id) REFERENCES Commentable(id)
);
CREATE TABLE UserPictures (
userpicture_id INT PRIMARY KEY, -- notice this is not auto-generated
...
FOREIGN KEY (userpicture_id) REFERENCES Commentable(id)
);
在向BlogPosts
或UserPictures
插入行之前,必须向Commentable
插入一个新行以生成新的伪节点ID。然后,在将内容插入相应的子类型表时,可以使用该生成的ID。
完成所有操作后,您可以依赖参照完整性约束。
答案 1 :(得分:1)
在 MySQL 5.7 中,您可以拥有一个单一的多态表并享受诸如多态外键之类的东西!
需要注意的是,从技术上讲,您需要将其实现为多个列上的多个 FK(每个具有评论的实体一个),但实现可以仅限于 DB 端(即您无需担心这些代码中的列)。
想法是使用 MySQL 的 Generated Columns:
CREATE TABLE comments (
id INT NOT NULL AUTO_INCREMENT,
foreign_id INT,
model TEXT,
commented_text TEXT,
generated_blogpost_id INT AS (IF(model = 'blogpost', foreign_id, NULL)) STORED,
generated_picture_id INT AS (IF(model = 'picture', foreign_id, NULL)) STORED,
PRIMARY KEY (id) ,
FOREIGN KEY (`generated_blogpost_id`) REFERENCES blogpost(id) ON DELETE CASCADE,
FOREIGN KEY (`generated_picture_id`) REFERENCES picture(id) ON DELETE CASCADE
)
您可以忽略 generated_*
列;它们会在添加或修改注释时由 MySQL 自动填充,并且为它们定义的 FK 将确保数据的一致性。
显然,它会影响大小要求和性能,但对于某些(大多数?)系统来说,它可以忽略不计,而且为了通过更简单的设计实现数据一致性而付出的代价是值得的。