【问题标题】:Implementing Comments and Likes in database在数据库中实现评论和喜欢
【发布时间】:2011-12-28 02:37:26
【问题描述】:

我是一名软件开发人员。我喜欢编码,但我讨厌数据库...目前,我正在创建一个网站,允许用户在该网站上将实体标记为 like(就像在 FB 中一样),tag 它和评论

我陷入了处理此功能的数据库表设计中。解决方案是微不足道的,如果我们只能对一种类型的事物(例如照片)执行此操作。但我需要为 5 种不同的东西启用此功能(目前,但我也假设随着整个服务的增长,这个数字会增长)。

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

问题是,如何正确、高效弹性地设计数据库,以便它可以存储不同的cmets,likes 用于不同的 tablestags。一些设计模式作为答案将是最好的;)

详细说明: 我有一个 table User 有一些用户数据,还有 3 个 tablesPhoto照片Articles文章Places地点。我想让任何登录的用户:

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

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

  • 用一些标签标记它们中的任何一个

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

1st 方法

a) 对于tags,我将创建一个table Tag [TagId, tagName, tagCounter],然后我将创建many-to-many 关系表格Photo_has_tagsPlace_has_tagArticle_has_tag

b) cmets 的计数相同。

c) 我将创建一个 LikedPhotos [idUser, idPhoto]LikedArticles[idUser, idArticle]LikedPlace [idUser, idPlace]likes 的数量将由 queries 计算(我认为这是不好的)。还有……

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


2nd方法

我将创建一个表ElementType [idType, TypeName == some table name] 将由管理员(我)使用可以喜欢评论的名称填充该表/em> 或标记。然后我会创建tables

a) LikedElement [idLike, idUser, idElementType, idLikedElement] 和 Comments 和 Tags 相同,每个都有适当的列。现在,当我想制作一张喜欢的照片时,我会插入:

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. 在元素 (Photo/Article/Place) 表中
  2. 通过选择 count()。

我希望我现在对这个问题的解释更彻底。

【问题讨论】:

  • 你考虑过 XML 吗?
  • 新手可以考虑像 neo4j 这样专为处理复杂连接而设计的图形数据库。

标签: mysql database design-patterns database-design


【解决方案1】:

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

对此的实体关系术语是“类别”(参见ERwin Methods Guide,部分:“子类型关系”)。类别符号为:

假设一个用户可以喜欢多个实体,同一个标签可以用于多个实体,但评论是特定于实体的,您的模型可能如下所示:


顺便说一句,“ER 类别”的实现大致有 3 种方式:

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

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

【讨论】:

  • 很好的答案,谢谢。我希望,我会设法实现它......我想知道 Django ORM 将如何处理映射它(或者我将如何自己做......但是,这是另一个问题;))但是,你能解释一下我,因为我认为我没有正确理解它 - 你为我画的(谢谢!)是你提到的第三种方法?
  • @Kokos 本质上,方法(3)意味着ENTITY是一个表,PHOTO是一个表,ARTICLE是一个表,PLACE是一个表。方法 (2) 表示 ENTITY 没有 no 表,方法 (1) 表示只有一个表。所有这些方法的存在(都有其优点和缺点)是典型 RDBMS 本身不支持表继承这一事实的不幸结果。
  • @tylerdurden 可能最简单的方法是在 USER 引用 PHOTO 中创建一个外键。这将独立于用户是否已经通过 LIKED_ENTITY 连接到照片。如果您想确保用户的照片必须通过 LIKED_ENTITY 进行链接 - 这是一个不同的“蠕虫罐头”,并且不容易仅通过声明性方式强制执行。
  • @BrankoDimitrijevic 为什么实体表 Photo, Article, Place 不能有自己的 PK 例如PhotoID、ArticleID 等,但还有另一列 Entity_ID 作为 FK?这是不必要的吗?
  • @Orion BIGINT 的最大值为 9223372036854775807。假设您每秒插入一行,您将在约 3000 亿年中用完可用值。当然,到那时您将能够移植到 128 位整数!
【解决方案2】:

既然你“讨厌”数据库,你为什么要尝试实现一个?相反,请向热爱和呼吸这些东西的人寻求帮助。

否则,请学会爱上您的数据库。精心设计的数据库可以简化编程、设计站点并使其持续运行更加顺畅。即使是经验丰富的 d/b 设计师也不会有完整和完美的远见:随着使用模式的出现或需求的变化,未来需要对架构进行一些更改。

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

您可能需要多次重构架构。这个是正常的。不要担心第一次就完美。只需使其功能足以对初始设计进行原型设计即可。如果您有足够的时间,请使用一些时间,然后删除模式并再次执行此操作。第二次总是更好。

【讨论】:

  • 因为需要自己实现。至少现在......而且,我认为这也许是开始喜欢数据库的好时机;)感谢您对存储过程的建议。有人知道,如果它们是由 Django ORM 自动映射的?
【解决方案3】:

这是一个普遍的想法 请不要太在意字段名称的样式,而更要注意关系和结构

此伪代码将获取 ID 为 5 的照片的所有 cmets
SELECT * FROM 操作
WHERE actions.id_Stuff = 5
AND actions.typeStuff="照片"
AND actions.typeAction = "评论"

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

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

【讨论】:

  • 我认为您甚至可能喜欢 cmets,因为在评论中单击“喜欢”链接。此查询将获得 ID 为 133 的评论(操作)之类的:SELECT * FROM actions WHERE actions.id=133 AND actions.typeStuff = "comment" AND actions.typeAction = "like"
  • 我一定会记住这个解决方案,以便进一步发布我的系统:)
  • 我有 2 个东西表 stuff1 和 stuff2...我按照此图进行操作,但使用此图时出现 sql 错误...stuff1、stuff2 是两个具有独立主键和操作表的独立表有一个列 id_stuff 引用这两个表 stuff1,stuff2。现在例如 stuff1 有 5 行, stuff2 有 10 行,当我尝试在操作表中添加 id_stuff 任何小于 5 的行时,可以说“3”它执行查询,因为在 stuff1 和stuff2,但如果我尝试添加 id_stuff 大于 5 的行...(继续下一条评论)
  • 如果要以这种方式实现点赞,通知用户新的点赞会更加困难。这将需要另一个表。
  • id_stuff 列如何在三个表中包含唯一值?
【解决方案4】:

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

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

【讨论】:

  • 如果您发布您的数据库图表。我可以画出关系。
【解决方案5】:

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

如果不喜欢需要较少表的那个

在这种情况下:

  1. 添加注释:您要么选择一个特定的多/多表,要么插入一个具有已知特定标识符的公共表中,我认为在您的第二种情况下,客户端代码会稍微简单一些。
  2. 为项目查找 cmets:这里似乎使用公用表稍微容易一些 - 我们只有一个按实体类型参数化的查询
  3. 查找某人关于某类事物的 cmets:两种情况下的简单查询
  4. 查找一个人关于所有事物的所有 cmets:无论哪种方式,这似乎都不太麻烦。

我认为您的“有区别的”方法选项 2 在某些情况下会产生更简单的查询,而在其他情况下似乎不会更糟,所以我会选择它。

【讨论】:

    【解决方案6】:

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

    有一天,您需要优化从这种结构中读取的内容。您可以轻松地在基础表上创建聚合表,并在写入时损失一些。

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

    【讨论】:

    • 更多的表意味着它的可维护性会降低。大多数 d/bs 可以对单个表进行分片。
    【解决方案7】:

    绝对要采用第二种方法,您有一个表并存储每一行​​的元素类型,它会给您更多的灵活性。基本上,当可以用更少的表在逻辑上完成某些事情时,使用更少的表几乎总是更好。关于您的特定情况,我现在想到的一个优势是,考虑您要删除某个用户的所有喜欢的元素,使用第一种方法,您需要为每种元素类型发出一个查询,但使用第二种方法可以完成只有一个查询或考虑何时要添加新元素类型,第一种方法涉及为每种新类型创建一个新表,但第二种方法您不应该做任何事情......

    【讨论】:

      猜你喜欢
      • 2013-05-26
      • 1970-01-01
      • 2016-03-24
      • 1970-01-01
      • 2013-11-05
      • 1970-01-01
      • 2018-04-14
      • 2016-11-09
      • 2020-10-07
      相关资源
      最近更新 更多