MySQL - 条件外键约束

发布于 2024-08-16 23:43:32 字数 509 浏览 8 评论 0原文

我的应用程序中有以下 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_id`) REFERENCES blogposts (`id`)
-- but only when model='blogpost'

I have following comments table in my app:

comments
--------
id           INT
foreign_id   INT
model        TEXT
comment_text TEXT
...

the idea of this table is to store comments for various parts of my app - it can store comments for blog post i.e.:

1|34|blogpost|lorem ipsum...

user picture:

2|12|picture|lorem ipsum...

and so on.

now, is there a way to force FOREIGN KEY constraint on such data?

i.e. something like this in comments table:

FOREIGN KEY (`foreign_id`) REFERENCES blogposts (`id`)
-- but only when model='blogpost'

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

葬心 2024-08-23 23:43:33

您正在尝试进行一种称为“多态关联”的设计。也就是说,外键可以引用多个相关表中任何一个中的行。

但外键约束必须精确引用一个表。您无法根据 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)
);

在将行插入 BlogPostsUserPictures 之前,您必须将新行插入 Commentable 以生成新的伪键 ID。然后,您可以在将内容插入到相应的子类型表时使用生成的 ID。

一旦完成所有这些,您就可以依赖引用完整性约束。

You're attempting to do a design that is called Polymorphic Associations. That is, the foreign key may reference rows in any of several related tables.

But a foreign key constraint must reference exactly one table. You can't declare a foreign key that references different tables depending on the value in another column of your Comments table. This would violate several rules of relational database design.

A better solution is to make a sort of "supertable" that is referenced by the 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)
);

Each of your content types would be considered a subtype of this supertable. This is analogous to the object-oriented concept of an interface.

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)
);

Before you can insert a row into BlogPosts or UserPictures, you must insert a new row to Commentable to generate a new pseudokey id. Then you can use that generated id as you insert the content to the respective subtype table.

Once you do all that, you can rely on referential integrity constraints.

じ违心 2024-08-23 23:43:33

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

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

这个想法是使用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 将确保数据的一致性。

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

In MySQL 5.7 you can have a single polymorphic table AND enjoy something like a polymorphic foreign key!

The caveat is that technically you will need to implement it as multiple FKs on multiple columns (one per each entity that has comments), but the implementation can be limited to the DB side (i.e. you will not need to worry about these columns in your code).

The idea is to use MySQL's 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
)

You can ignore the generated_* columns; they will be populated automatically by MySQL as comments are added or modified, and the FKs defined for them will ensure data consistency as expected.

Obviously it would impact both the size requirements and performance, but for some (most?) systems it would be negligible, and a price worth paying for achieving data consistency with a simpler design.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文