在数据库中实现评论和喜欢


145

我是软件开发人员。我喜欢编写代码,但我讨厌数据库...目前,我正在创建一个网站,允许用户在该网站上将一个实体标记为喜欢(如FB中),对其进行标记评论

我在处理此功能的数据库表设计上陷入了困境。如果我们只能对一种类型的事物(例如照片)执行此操作,那么解决方案将是微不足道的。但是我需要为5种不同的功能启用此功能(现在,但我还假设随着整个服务的增长,这个数字可以增长)。

我在这里找到了一些类似的问题,但是没有一个令人满意的答案,所以我再次问这个问题。

问题是,如何正确,有效弹性地设计数据库,以便它可以存储不同表的注释,喜欢不同表的注释以及它们的标签。一些设计模式作为答案将是最好的;)

详细描述:我有一个 User与一些用户数据,以及3个Photo照片Articles文章Places的地方。我想使任何登录的用户能够:

  • 评论这三个表中的任何一个

  • 将其中任何一个标记为喜欢

  • 用标签标记其中的任何一个

  • 我还想计算每个元素的点赞次数以及使用该特定标签的次数。

1 的做法

a)对于标签,我将创建一个 Tag [TagId, tagName, tagCounter],然后我会创造很多一对多的关系为:Photo_has_tagsPlace_has_tagArticle_has_tag

b)评论数相同。

三)我将创建一个 LikedPhotos [idUser, idPhoto]LikedArticles[idUser, idArticle]LikedPlace [idUser, idPlace]。数喜欢将计算查询(其中,我认为是坏的)。和...

我真的不喜欢这个设计的最后一部分,对我来说很难闻;)


2 的方法

我将创建一个表ElementType [idType, TypeName == some table name],该表将由管理员(me)填充,并带有可被喜欢注释标记的名称。然后我将创建

a)LikedElement [idLike, idUser, idElementType, idLikedElement]注释和标签的内容相同,每个注释都有正确的列。现在,当我想制作一张喜欢的照片时,我将插入:

typeId = SELECT id FROM ElementType WHERE TypeName == 'Photo'
INSERT (user id, typeId, photoId)

和地方:

typeId = SELECT id FROM ElementType WHERE TypeName == 'Place'
INSERT (user id, typeId, placeId)

依此类推...我认为第二种方法更好,但是我也觉得这种设计也缺少一些东西...

最后,我还想知道存储该元素多少次的最佳计数器是哪个。我只能想到两种方式:

  1. 在element(Photo/Article/Place)表中
  2. 通过选择count()。

我希望我对这个问题的解释现在更加全面。


您考虑过XML吗?
CodyBugstein 2014年

1
我很少能找到像我这样100%的问题,您的问题非常完整!谢谢@Kokos。
aderchox

Answers:


194

最可扩展的解决方案是仅具有一个“基本”表(连接到“喜欢”,标签和注释),并从其“继承”所有其他表。添加一种新的实体只需添加一个新的“继承”表-然后它会自动插入整个like / tag / comment机制。

实体关系术语是“类别”(请参阅​​《 ERwin方法指南》的“子类型关系”部分)。类别符号为:

类别

假设用户可以喜欢多个实体,同一个标记可以用于多个实体,但是注释是特定于实体的,则您的模型应如下所示:

ER图


顺便说一句,大约有3种方法可以实现“ ER类别”:

  • 一个表中的所有类型。
  • 所有具体类型均在单独的表格中。
  • 所有具体和抽象类型都放在单独的表中。

除非您有非常严格的性能要求,否则第三种方法可能是最好的(这意味着物理表与上图中的实体1:1匹配)。


2
很好的答案,谢谢。我希望,我将设法实现它……而且我想知道Django ORM将如何处理它(或者我将如何独自完成它……但是,这是另一个问题;)))但是,你能解释一下吗我,因为我认为我不太了解-您为我画的东西(谢谢!)是您提到的第三种方法?
科科斯

2
@Kokos本质上,方法(3)表示ENTITY是一个表,PHOTO是一个表,ARTICLE是一个表,PLACE是一个表。方法(2)表示没有 ENTITY表,而方法(1)则表示只有一个表。所有这些方法(各有优缺点)的存在是不幸的结果,因为典型的RDBMS本身并不支持表继承。
Branko Dimitrijevic

1
+1感谢您对“类别”的出色解释和参考。我本打算在这附近发布一个问题,但您在这里回答了。
Andy holaday

2
@BrankoDimitrijevic为什么实体表Photo,Article,Place不能拥有自己的PK,例如PhotoID,ArticleID等,但又要为Entity_ID提供另一列作为FK?这是不必要的吗?
第一卷

3
@Orion的最大值为BIGINT9223372036854754775807。假设每秒插入一行,您将在大约3,000亿年的时间里耗尽可用值。到那时,您当然可以移植到128位整数!
Branko Dimitrijevic 2014年

22

既然您“讨厌”数据库,那么为什么要尝试实现一个数据库呢?相反,请喜欢和呼吸这种东西的人寻求帮助。

否则,请学习爱您的数据库。精心设计的数据库可简化编程,站点设计并简化其继续运行。即使是经验丰富的d / b设计师,也不会具有完整和完善的预见能力:随着使用模式的出现或需求的变化,将需要对方案进行一些后续的更改。

如果这是一个单一项目,则使用存储过程将数据库接口编程为简单的操作:add_user,update_user,add_comment,add_like,upload_photo,list_comments等。请勿将架构甚至嵌入到一行代码中。通过这种方式,可以在不影响任何代码的情况下更改数据库模式:只有存储过程才应该知道该模式。

您可能必须多次重构架构。这是正常的。不用担心第一次就能做到完美。只需使其功能足以原型化初始设计即可。如果时间充裕,请使用它,然后删除架构并再次执行。它总是更好的第二次。


2
因为我需要自己实施。至少到目前为止,而且,我认为也许是个开始喜欢数据库的好时机;)谢谢您关于存储过程的建议。有人知道它们是否由Django ORM自动映射吗?
Kokos

6
我爱你的最后一句话- 第二次总是更好。
刘易斯

2
第二次总是更好。是的
加默

20

这是一个一般性的想法,请不要过多关注字段名称的样式,而应更多地关注关系和结构

在此处输入图片说明

该伪代码将获取ID为5的照片的所有注释
*从行为
WHERE actions.id_Stuff = 5
AND actions.typeStuff =“ photo”
AND actions.typeAction =“ comment”

此伪代码将获得所有喜欢或喜欢ID为5的照片的用户
(您可以使用count()来获得喜欢的数量)

SELECT * FROM actions  
WHERE actions.id_Stuff = 5  
AND actions.typeStuff="photo"  
AND actions.typeAction = "like"  

我认为您甚至可能喜欢评论,例如单击评论中的“喜欢”链接。此查询将获得ID为133的评论(操作)的喜欢: SELECT * FROM actions WHERE actions.id=133 AND actions.typeStuff = "comment" AND actions.typeAction = "like"
user964260 2011年

1
我一定会记得这个解决方案,可以进一步发布我的系统:)
Kokos

我有2个填充表stuff1和stuff2 ...我遵循此图,但是使用此表时存在sql错误... stuff1,stuff2是两个具有各自独立主键的独立表,而操作表具有列id_stuff引用这两个表格stuff1,stuff2。现在,例如,stuff1有5行,stuff2有10行,当我尝试在具有id_stuff的操作表中添加行时,小于5的任何内容都可以说'3',它会执行查询,因为在stuff1和stuff2,但如果我尝试添加id_stuff大于5的行...(继续下
一条

1
如果要以这种方式实施喜欢,则使通知新喜欢的用户更加困难。这将需要另一个表。
格雷格L

4
id_stuff列将如何在三个表的每一个中包含唯一值?
卷一

0

据我所理解。需要几个表。它们之间存在多对多的关系。

  • 该表存储带有身份字段的用户数据,例如姓名,姓氏,出生日期。
  • 该表存储数据类型。这些类型可能是照片,共享,链接。每个类型必须具有唯一的表。因此,它们的各个表与该表之间存在关系。
  • 每个不同的数据类型都有其表。例如状态更新,照片,链接。
  • 最后一个表是用于存储ID,用户ID,数据类型和数据ID的多对多关系。

如果发布数据库图。我可以得出关系。
erencan

0

查看您将需要的访问模式。他们中的任何一个似乎使我的一个或另一个设计特别困难或效率低下吗?

如果不喜欢需要较少桌子的桌子

在这种情况下:

  1. 添加评论:您可以选择一个特定的“很多/很多”表,也可以插入一个具有已知特定标识符的通用表,以表示喜欢的内容,我认为在第二种情况下,客户端代码会稍微简单一些。
  2. 查找项目的注释:在这里,使用公用表似乎稍微容易一些-我们只有一个按实体类型参数化的查询
  3. 查找某人对某事的评论:无论哪种情况都可以简单查询
  4. 找到一个人对所有事物的所有评论:无论哪种方式,这似乎都有些粗糙。

我认为您的“区别对待”方法(选项2)在某些情况下会产生更简单的查询,而在其他情况下似乎并不差很多,因此我会继续使用。


0

绝对要采用第二种方法,即您有一个表并存储每一行​​的元素类型,它将为您提供更大的灵活性。基本上,当逻辑上可以用更少的表来完成某件事时,使用更少的表几乎总是更好的选择。我现在想到的一个关于您的特殊情况的优点是,考虑您要删除某个用户的所有喜欢的元素,第一种方法需要针对每种元素类型发出一个查询,而第二种方法可以完成仅使用一个查询或考虑何时要添加新的元素类型,第一种方法涉及为每种新类型创建一个新表,但是第二种方法则无需执行任何操作...


-1

考虑使用每个实体的表进行注释等。更多表-更好的分片和缩放。对于我所知道的所有框架,控制许多相似的表不是问题。

有一天,您需要优化来自这种结构的读取。您可以轻松地在基本表上创建令人赞叹的表,而在写入时会浪费一点时间。

一张有字典的大桌子有一天可能变得无法控制。


表格越多,它的维护性就越差。单个表可以由大多数d / b分片。
wallyk
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.