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


10

我有3张桌子:

  • 发布
  • 喜欢

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

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

         1:N
发表---------- <喜欢

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

逻辑是:

  • 1人可以发表很多帖子。

  • 1个帖子有很多赞。

  • 1个人可以喜欢许多帖子(创建的人不能喜欢自己的帖子)。

如何删除这种循环设计?还是我的数据库设计错误?

Answers:


10

商业规则

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

  • A Person创建零一个或多个 Posts
  • A Post收到零一个或多个 Likes
  • 一个Person清单显示零一个或多个 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. 一个检查约束,当然,但是,这种方法排除了MySQL的,因为它没有在这个平台至今实现,你可以看到在这里这里

  2. ACID事务内的代码行

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

选项B

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

这种方法还可以确保帖子只能被同一个人一次喜欢。


笔记

1.信息建模集成定义(IDEF1X)是一种高度推荐的数据建模技术,该技术已于1993年12月由美国国家标准技术研究院(NIST)定义为标准

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

3. 角色名称是指分配给外键属性的符号,目的是在其对应的实体类型的上下文中表达该属性的含义。EF Codd博士自1970年以来就在他的开创性论文《大型共享数据库的数据关系模型》中建议使用角色命名。就其本身而言,IDEF1X(保持忠实于关系实践)也提倡此过程。


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

特别要注意的是,帖子必须有一个author(NOT NULL),而点赞的存在是可选的。但是,对于现有的喜欢,post并且都person 必须同时引用两者(由强制执行,PRIMARY KEY这会NOT NULL自动使两列(您可以明确地,冗余地添加这些约束)),因此匿名喜欢也是不可能的。

n:m实现的详细信息:

预防自我

您还写道:

(创建的人不能喜欢自己的帖子)。

尚未在上述实现中强制执行。您可以使用触发器
或以下更快/更可靠的解决方案之一:

坚如磐石的成本

如果需要坚如磐石,则可以将FK从扩展likes到,post以包括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)
);

这在以下方面还需要一个冗余UNIQUE约束post

ALTER TABLE post ADD CONSTRAINT post_for_fk_uni UNIQUE (author_id, post_id);

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;

将“ public”替换为表的实际架构。
CHECK约束中使用此函数:

ALTER TABLE likes ADD CONSTRAINT no_self_like_chk
   CHECK (f_author_id_of_post(post_id) <> person_id) NOT VALID;

4

我认为您很难确定这一点,因为您如何陈述自己的业务规则。

人和职位是“对象”。Like是一个动词。

您实际上只有2个动作:

  1. 一个人可以创建一个或多个帖子
  2. 许多人可以喜欢许多职位。(最后2条语句的汇总)

人们喜欢帖子图

“喜欢”表将person_id和post_id作为主键。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.