【问题标题】:Users Rating update, recalculation用户评分更新、重新计算
【发布时间】:2012-09-22 01:11:37
【问题描述】:

我有一个包含字段 id、投票(针对每个用户)、评分的表格。

任务:根据对他和其他人的投票计算用户评分。也就是我每次更新字段投票需要重新计算字段评分。

这意味着有些人可以排在第三位。投票给他,他将获得第二名,反之亦然 - 从 2 到 3。(在评分字段中)

如何解决这个问题?每次更新字段来统计用户对php的评分,在mysql中做大量的更新查询是非常昂贵的。

【问题讨论】:

  • 你能举一个你的表格值的例子吗
  • 嗯..我没有基地。例如(id, votes, rating) 1, 124, 2 2, 85, 3 3, 999, 1

标签: php mysql


【解决方案1】:

如果您想在没有评级列的情况下通过选择获得评级,那么这就是方法。但是,从性能的角度来看,我不能保证这将是您的最佳选择。它的工作方式是,如果两个用户的票数相同,他们将获得相同的评分,然后它将跳过必要的数字以获得下一个不同的评分:

set @rating:=0;
set @count:=1;

select id,
case when @votes<>votes then @rating:=@rating+@count
else @rating end as rating,
case when @votes=votes then @count:=@count+1
else @count:=1 end as count,
@votes:=votes as votes
from t1
order by votes desc

sqlfiddle

这为您提供了一个可以忽略的额外列,或者您可以将此选择包装到子查询中并具有:

select t2.id,t2.votes,t2.rating from (
select id,
case when @votes<>votes then @rating:=@rating+@count
else @rating end as rating,
case when @votes=votes then @count:=@count+1
else @count:=1 end as count,
@votes:=votes as votes
from t1
order by votes desc) as t2

但是 sqlfiddle 奇怪地给出了不一致的结果,所以你必须做一些测试。如果有人知道这是为什么,我很想知道原因。

如果您只想获得一个用户的评分,那么执行子查询选项并在from 之后使用where 应该会给您想要的结果。 sqlfiddle - 但同样,结果不一致,运行几次,有时它的评分为 10 次,有时为 30 分。我认为在您的数据库中进行测试以查看会发生什么将是最好的。

【讨论】:

  • 太好了,很高兴我能帮上忙。如果您需要更多帮助,请告诉我,否则请在箭头旁边打勾以接受正确答案。
  • mysql触发器怎么样?它们的工作速度比普通查询快吗?
  • @KirillSperansky 我不是触发器方面的专家,但我相信在触发器中完成这项工作并不容易,而且你会在每次更新后运行它,最终可能会有一个体面的性能成本。触发器作用于正在更新的行,所以我不知道您将如何更新整个表,也许是让触发器运行stored procedure。在这种情况下,您最好使用cron job 并经常更新表格,但是您的结果不会总是最新的。
  • @KirillSperansky 使用select 的好处是您可以使结果保持最新,并且case 计算的额外性能成本可能会非常小,当您必须这样做时无论如何都要搜索整个表格并且拥有相当轻量级的updates。如果您确实想要一个评级列,或者与selects 相比,updates 的数量相对较少,那么可能值得提出一个关于如何使用触发器专门执行您想要的操作的新问题。我也有兴趣看看。
【解决方案2】:

这取决于很多因素

  1. 您是否有一个呈指数级增长的大型系统?
  2. 您需要投票数据来进行历史报告吗?
  3. 用户投票时需要注册吗?
  4. 此系统在整个系统生命周期中是否仅用于一种投票类型,还是会针对不同主题进行更多投票?

如果所有答案都是,那么您当前的更新方法就可以正常工作。无论如何,只要确保您应用了最佳编码和 MySQL 表实践即可。

假设您的大部分或所有答案都是YES,那么我会提出以下建议:

  • 每次投票时INSERT 都会记录到您的表格中
  • 使用INSERT,添加时间戳,如果不可能,添加用户ID,然后可能是IP地址/位置
  • 从 vote_subject 表中指定一个主题 ID 作为外键。在此表中存储投票的主题和日期
  • 现在您可以创建一个SELECT 语句来计算选票和评分。投票计数列表顶部的人将在SELECT 中获得评级 1。此外,您可以按主题、每天、每个用户进行过滤,还应该能够根据所需的结果确定音量。

所有这一切当然取决于您的系统将来如何扩展。这可能有点矫枉过正,但值得考虑。

【讨论】:

  • 1.假设是具有一百万行用户的用户表。每个人都需要根据票数进行评分。也就是说,从一到一百万。 2.不,我需要在用户页面上显示当前用户评分。 3. 是的,需要注册。 4. 一种投票类型。当某些用户为当前用户投票时,我必须更新投票字段。我需要一个表(或表中的一个字段),它会发给每个用户评分从一个到用户数。并且为用户投票(这很常见),有必要通过相对于其他用户向上或向下移动来抵消可能的评分和评分。
【解决方案3】:

是的,聚合很昂贵。您可以每五分钟左右更新一次排名表并从那里查询。您现在可能已经进行的查询是这样的:

select id, count(*) as votes
from users
group by id
order by votes desc

【讨论】:

  • 关于升级,比如每分钟,我一直在想。
【解决方案4】:

不使用字段 id、votes 和 rating,而是将表更改为具有字段 id、rating_sum 和 rating_count。每次你有一个新的评分时,你都会像这样查询数据库:

    "UPDATE `ratings` SET `rating_count` = `rating_count` + 1, `rating_sum` = `rating_sum`+ $user_rating WHERE `id` = $id"

现在评分只是平均值 -> rating_sum / rating_count。不需要带有评分的字段。

另外,为了防止用户多次评分,您可以创建一个名为 rating_users 的表,该表将有两个外键 users.id 和 rating.id。主键将是(users.id,ratings.id)。因此,每次用户首先尝试评分时,您都会检查此表。

【讨论】:

  • 如果您只想处理总体评分平均值,这很好,但大多数评分系统都希望您能够查看单个用户对项目的评分。
  • 我不需要平均评分。我需要 1、2、3、4 的评分……因为每个用户的评分都是唯一的,不能与其他用户的评分相同。用户会互相欣赏。和评级是基于此。
  • 您可以通过在 rating_users 表中添加一个额外字段来解决此问题,该字段将是特定用户的评分。
  • 但是我如何重新计算每个更新查询(投票字段)的用户评分?
  • 嗯,你想知道每个用户的评分和评分是多少,对吧?为此,您需要 2 个表:1) Ratings(id, rating_count, rating_sum) 和 2) Ratings_users(user_id, rating_id, user_rated)。对于每个评级,您都会更新 Ratings 表,就像我之前所说的,然后是 Ratings_Users。当您想知道评分时,您查询数据库并获取 rating_sum / rerating_count,当您想知道用户评分时,您查询他的 id 和评分的 id 到另一个表中以获取它。您不需要进行任何重新计算,因为您根本不将平均值存储在数据库中。
【解决方案5】:

我建议在查询数据时这样做。它会简单得多。按票数降序排列。

也许创建一个视图并在查询数据时使用该视图。

你可以试试这样的:

SET @rank := 0

select id, count(*) as votes, @rank := @rank + 1
from users
group by id
order by votes desc

或者

SET @rank := 0

select id, votes, @rank := @rank + 1
from users
order by votes desc

【讨论】:

  • 但是如果我必须为用户显示当前评分,我怎么能在没有评分字段的情况下做到这一点?
  • hm.. 可以这样做:SELECT COUNT(*) FROM users WHERE id=$id AND votes>=(votes for this user) ?
  • 我同意这一点。我认为在数据库中复制数据总是会给你带来潜在的错误。我宁愿在查询中进行计算,而不是使用“平均评分”字段,因为计算平均值所需的所有数据都已存储。当然,这样做的最大缺点是它会使你的代码更难读/写。所以这不是一个硬性规定。
  • 我不需要平均评分。我需要 1、2、3、4 的评分……因为每个用户评分都是唯一的,不能与其他用户的评分相同。
猜你喜欢
  • 2012-10-07
  • 1970-01-01
  • 2018-07-24
  • 2012-11-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-14
  • 2013-08-07
相关资源
最近更新 更多