如何避免 3 个表之间的循环依赖(循环引用)?

Rag*_*agu 11 mysql postgresql database-design design-pattern

我有3张桌子:

  • 人们
  • 邮政
  • 喜欢

当我设计 ER 模型时,它具有循环依赖关系:

         1:N
人 --------< 帖子

         1:N
发帖 ----------< 点赞

         1:N
人们 --------< 喜欢

逻辑是:

  • 1个人可以有很多帖子。

  • 1个帖子有很多赞。

  • 1个人可以点赞多个帖子(创建的人不能点赞自己的帖子)。

我怎样才能消除这种循环设计?还是我的数据库设计错了?

MDC*_*CCL 11

商业规则

让我们对您提出的业务规则进行一些改写:

  • APerson创建零一或多 Posts
  • APost接收零一或多 Likes
  • APerson表示零一或多 Likes每个都一个特定的 Post.

逻辑模型

然后,从这组断言中,我推导出了图 1中所示的两个逻辑级别的IDEF1X [1]数据模型。

图 1 - 人员和帖子数据模型

选项 A

正如可以在选项A型号见,PersonId 迁移[2]PersonPost作为外键(FK),但它接收到的角色名称[3]AuthorId具有和该属性构成,一起PostNumber,主键(PK)的所述Post实体的类型。

我假设Like只能与一个特定的连接存在Post,所以我已经设置了LikePK,3个不同的属性包括:PostAuthorIdPostNumberLikerId。的组合PostAuthorIdPostNumber是一个FK,使适当的参考PostPK。LikerId反过来,是与 建立适当关联的 FK Person.PersonId

在这种结构的帮助下,您可以确保一个坚定的人只能Like对同一个Post实例表现出一个事件。

防止帖子作者喜欢他自己的帖子的方法

既然你希望允许一个人能像他的可能性/她撰写的帖子,一旦在实施阶段,你应该建立比较值的方法Like.PostAuthorId与价值Like.LikerId在每个插入的企图。如果所述值匹配,(a)你拒绝插入,如果它们不匹配(b)你让过程继续。

为了在您的数据库中完成此任务,您可以使用:

  1. A CHECK CONSTRAINT但是,当然,此方法不包括 MySQL,因为到目前为止它尚未在此平台中实现,如您在此处此处所见。

  2. 代码行ACID事务

  3. 代码行一个内触发,这可能会返回指示违反规则的企图自定义消息。

选项 B

如果作者不是在您的业务领域中以主要方式标识帖子的属性,您可以采用类似于选项 B 中描述的结构。

这种方法还确保一个帖子只能被同一个人点赞一次。


笔记

1. 信息建模集成定义 ( IDEF1X ) 是一种非常值得推荐的数据建模技术,于1993 年 12 月被美国国家标准与技术研究院 ( NIST )定义为标准

2. IDEF1X 将键迁移定义为“将父实体或通用实体的主键作为外键放置在其子实体或类别实体中的建模过程”。

3.角色名称是分配给外键属性的符号,目的是在其对应实体类型的上下文中表达该属性的含义。自 1970 年以来,EF Codd 博士在题为“大型共享数据库的数据关系模型”的开创性论文中推荐使用角色命名。就其本身而言,IDEF1X——在关系实践方面保持忠诚——也提倡这种程序。


Erw*_*ter 6

我没有看到这里有任何循环。这些实体之间存在人员职位以及两种独立的关系。我认为喜欢是这些关系之一的实现。

  • 一个人可以写很多帖子,一个帖子是一个人写的: 1:n
  • 一个人可以喜欢很多帖子,一个帖子可以被很多人喜欢:n:m
    n:m 关系可以用另一个关系来实现:likes

基本实现

PostgreSQL 中的基本实现可能如下所示:

CREATE TABLE person (
  person_id serial PRIMARY KEY
, person    text NOT NULL
);

CREATE TABLE post (
  post_id   serial PRIMARY KEY
, author_id int NOT NULL  -- cannot be anonymous
     REFERENCES person ON UPDATE CASCADE ON DELETE CASCADE  -- 1:n relationship
, post      text NOT NULL
);

CREATE TABLE likes (  -- n:m relationship
  person_id int REFERENCES person ON UPDATE CASCADE ON DELETE CASCADE
, post_id   int REFERENCES post ON UPDATE CASCADE ON DELETE CASCADE
, PRIMARY KEY (post_id, person_id)
);
Run Code Online (Sandbox Code Playgroud)

特别要注意的是,一个帖子必须有一个作者(NOT NULL),而喜欢的存在是可选的。然而,对于现有的喜欢,post并且都person 必须被引用(由自动生成PRIMARY KEY两列的强制执行NOT NULL(您可以显式地、冗余地添加这些约束),因此匿名喜欢也是不可能的。

n:m 实现的详细信息:

防止自我喜欢

你还写道:

(被创造的人不能喜欢他自己的帖子)。

上面的实现中还没有强制执行。您可以使用触发器
或者这些更快/更可靠的解决方案之一:

坚如磐石的成本

如果它需要坚如磐石,您可以将 FK 从 扩展likespost以包含author_id冗余。然后你可以用一个简单的CHECK约束来排除乱伦。

CREATE TABLE likes (
  person_id int REFERENCES person ON UPDATE CASCADE ON DELETE CASCADE
, post_id   int 
, author_id int NOT NULL
, CONSTRAINT likes_pkey PRIMARY KEY (post_id, person_id)
, CONSTRAINT likes_post_fkey FOREIGN KEY (author_id, post_id)
     REFERENCES post(author_id, post_id) ON UPDATE CASCADE ON DELETE CASCADE
, CONSTRAINT no_self_like CHECK (person_id <> author_id)
);
Run Code Online (Sandbox Code Playgroud)

需要一个另外的冗余UNIQUE约束post

ALTER TABLE post ADD CONSTRAINT post_for_fk_uni UNIQUE (author_id, post_id);
Run Code Online (Sandbox Code Playgroud)

author_id首先提供了一个有用的索引

相关答案更多:

CHECK限制更便宜

以上面的“基本实现”为基础。

CHECK约束意味着是不可变的。引用其他表进行检查从来都不是一成不变的,我们在这里有点滥用这个概念。我建议声明约束NOT VALID以正确反映这一点。细节:

一个CHECK约束似乎在这种特殊情况下合理的,因为一个帖子的作者似乎是永远不会改变的属性。确定不允许对该字段进行更新。

我们伪造一个IMMUTABLE函数:

CREATE OR REPLACE FUNCTION f_author_id_of_post(_post_id int)
  RETURNS int AS
'SELECT p.author_id FROM public.post p WHERE p.post_id = $1'
LANGUAGE sql IMMUTABLE;
Run Code Online (Sandbox Code Playgroud)

用表的实际模式替换“public”。
CHECK约束中使用此函数:

ALTER TABLE likes ADD CONSTRAINT no_self_like_chk
   CHECK (f_author_id_of_post(post_id) <> person_id) NOT VALID;
Run Code Online (Sandbox Code Playgroud)