67

我的应用程序中有下comments表:

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'
4

2 回答 2

120

您正在尝试进行一种称为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)
);

在向BlogPostsor中插入一行之前UserPictures,您必须插入一个新行Commentable来生成一个新的伪键 id。然后,您可以在将内容插入相应的子类型表时使用生成的 id。

一旦你完成了所有这些,你就可以依赖参照完整性约束。

于 2010-01-04T23:23:40.500 回答
1

在 MySQL 5.7 中,您可以拥有一个多态表并享受多态外键之类的东西!

需要注意的是,从技术上讲,您需要在多个列上将其实现为多个 FK(每个具有注释的实体一个),但实现可以仅限于 DB 端(即,您无需担心这些列在您的代码)。

这个想法是使用 MySQL 的生成列:

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 将确保数据的一致性。

显然,它会影响大小要求和性能,但对于某些(大多数?)系统来说,它可以忽略不计,并且为了通过更简单的设计实现数据一致性而付出的代价是值得的。

于 2021-05-26T15:31:17.630 回答