如何在数据库中建模产品评分?

23
在数据库中存储产品评分的最佳方法是什么?我有以下两种方案(简化版,并假设使用MySQL数据库):

在产品表中创建两列,分别存储所有投票数和总投票数的和。使用这两列在运行时或使用查询来获取平均值。

这个方法意味着我只需要访问一个表,简化了事情。

通过创建额外的表来存储评分数据来规范化数据。

这将评分数据隔离到一个单独的表中,使产品表提供可用产品的数据。尽管这需要联接或单独查询评分。

哪种方法是最好的,规范化的还是非规范化的?

3个回答

43

为了保持数据的动态性,强烈建议使用不同的表来存储评分。不要担心有数百个(或数千个或数万个)条目,对于数据库来说那都是小菜一碟。

建议如下:

products

  • id
  • name
  • etc

products_ratings

  • id
  • productId
  • rating
  • date(如果需要)
  • ip(如果需要,例如防止重复评分)
  • etc

检索产品1234的所有评分:

SELECT pr.rating
FROM products_ratings pr
INNER JOIN products p
  ON pr.productId = p.id
  AND p.id = 1234

产品 1234 的平均评分:

SELECT AVG(pr.rating) AS rating_average -- or ROUND(AVG(pr.rating))
FROM products_ratings pr
INNER JOIN products p
  ON pr.productId = p.id
  AND p.id = 1234;

同时获取产品列表及其平均评分同样简单:

SELECT
  p.id, p.name, p.etc,
  AVG(pr.rating) AS rating_average
FROM products p
INNER JOIN products_ratings pr
  ON pr.productId = p.id
WHERE p.id > 10 AND p.id < 20 -- or whatever
GROUP BY p.id, p.name, p.etc;

1
我刚想到一个问题:如果我把这个变成一个“评论”表,那么很有可能大多数用户只会投票,而不一定添加评论。这将在表格中留下许多应该填写评论标题和评论文本的空单元格。这是一个问题吗? - Mohamad
3
@Mel:你可以像Tom建议的那样创建另一张表。但是,添加评分或添加评分和一些文本是非常相似的事情。在这种情况下,我会将它们合并到单个表中,以防止以后出现冗余,并且因为将它们拆分开来没有真正的优势。空列不占用空间也不影响速度,这是完全可以的(只要它们有适当的功能,这在这里是成立的)。这与向产品表添加“注释”列相同,但并不是每个产品都可能具有或需要注释。 - Alec
先生,我有同样的疑问,我想问一下是否应该将rating_id和product_id作为复合键,以减少冗余(用户不能再次对单个产品进行评分,如果它再次评分,则先前的查询将被更新而不是新插入。那么我该怎么做呢? - VJain
使用NoSQL数据库并将评分列表作为嵌入式数据实际上是一个好主意吗?哪种更适合扩展? - Dhia
1
我在这里看到一个问题,比如说我需要列出所有产品,而评分可以为0,也就是说某个产品没有评分。我们应该如何处理? - Abilash Arjunan
显示剩余3条评论

6

我知道我的回答不是你实际要求的,但你可能希望有机会促进新产品与你的系统几乎永远无法击败旧产品。比如说,你得到了一个99%评级的产品,如果按照最高评级的产品排序,新产品很难获得高评级。


1
David,我通过取平均评分(voteSum/voteCount)来规避这个问题。如果我决定强调最新产品,我可以先按发布日期排序,然后再按评分排序。但一般来说,我不关心产品的年龄。 - Mohamad
一个值得考虑的重要点! - Salih Kavaf

5
不要存储每个评分的记录,除非你确切地需要它们。这种情况的一个例子可能是心理实验,旨在分析评分者自身的特定属性。因此,是的!你必须像将每个评分存储在单独的记录中一样疯狂。
现在,来到解决方案,向产品表中添加两列:AverageRatingRateCount。你会在其中存储什么?假设你已经计算出了两个数字的平均值:2和3,即2.5;如果有一个新的评分为10,那么你将把平均数(2.5)乘以评分计数(在这种情况下为2)。现在,你有了5。把这个结果加上新的评分值(10),然后把结果除以3。
让我们用一个简单的公式总结以上所有内容:
(AverageRating * RateCount + NewRateValue) / (RateCount + 1)

(previous_average * previous_count - previous_rating + new_rating) / previous_count

这里的previous_average是之前的平均值,previous_count是之前的评分个数,previous_rating是用户修改前的评分,new_rating是用户修改后的评分。将这个公式应用于修改删除操作可以确保始终计算出正确的平均评分。

(AverageRating * RateCount - OldRateValue + NewRateValue) / RateCount

References

https://math.stackexchange.com/a/106314


高效选项 - Teodor Scorpan
如果用户更新他的评分,它会如何工作?例如:从3改为2? - Abhishek Ginani
让用户更新他们的评分需要首先存储该特定评分的单独记录。我的解决方案适用于不涉及审查、编辑或删除的情况。 - Salih Kavaf

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接