Answers:
让我们对您提出的业务规则进行一些改写:
Person
创建零一个或多个 Posts
。Post
收到零一个或多个 Likes
。Person
清单显示零一个或多个 Likes
,每个都与一个特定的有关 Post
。然后,从这样的一组断言中,我得出了图1所示的两个逻辑级别IDEF1X [1]数据模型。
正如可以在选项A型号见,PersonId
迁移[2]从Person
以Post
作为外键(FK),但它接收到的角色名称[3]的AuthorId
具有和该属性构成,一起PostNumber
,主键(PK)的所述Post
实体的类型。
我假设Like
只能与一个特定的连接存在Post
,所以我已经设置了Like
PK,3个不同的属性包括:PostAuthorId
,PostNumber
和LikerId
。的组合PostAuthorId
和PostNumber
是一个FK,使适当的参考Post
PK。LikerId
反过来是FK,它与建立了适当的关联Person.PersonId
。
借助这种结构,可以确保确定的人员只能Like
在同一Post
实例中出现一次。
由于您不希望某个人喜欢他/她撰写的文章,因此在实施阶段,您应该建立一种方法,将每次插入尝试中的值Like.PostAuthorId
与值进行比较Like.LikerId
。如果所述值匹配,则(a)您拒绝插入,如果它们不匹配(b),则让过程继续。
为了在数据库中完成此任务,可以使用:
如果作者不是主要标识您业务领域中帖子的属性,则可以采用类似于选项B中所述的结构。
这种方法还可以确保帖子只能被同一个人一次喜欢。
1.信息建模集成定义(IDEF1X)是一种高度推荐的数据建模技术,该技术已于1993年12月由美国国家标准技术研究院(NIST)定义为标准。
2. IDEF1X将密钥迁移定义为“将父或通用实体的主键放在其子或类别实体中作为外键的建模过程”。
3. 角色名称是指分配给外键属性的符号,目的是在其对应的实体类型的上下文中表达该属性的含义。EF Codd博士自1970年以来就在他的开创性论文《大型共享数据库的数据关系模型》中建议使用角色命名。就其本身而言,IDEF1X(保持忠实于关系实践)也提倡此过程。
我看不到这里有什么循环的。这些实体之间存在人员和职位以及两个独立的关系。我将喜欢视为这些关系之一的实现。
1:n
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;
我认为您很难确定这一点,因为您如何陈述自己的业务规则。
人和职位是“对象”。Like是一个动词。
您实际上只有2个动作:
“喜欢”表将person_id和post_id作为主键。