我正在构建一个问答平台,人们可以像这里或Reddit一样向上投票/向下投票。
$questions =
Question::select([
'questions.*',
'users.name AS username'
])
->leftJoin('questions_votes', 'questions_votes.question_id', '=', 'questions.id')
->leftJoin('users', 'users.id', '=', 'questions.user_id')
->groupBy('questions.id')
->get();
通过这个查询,我得到了每个问题的总数、创建者的用户名和问题数据本身。
所有投票都存储在questions_votes
(列:id, user_id, upvote
(中,其中upvote
可以是1表示赞成票,也可以是-1表示反对票。
从性能角度来看,让用户投票数据的最佳方法是什么?我应该…吗
- 在我的
$questions
-查询中添加一个子查询,以检查该用户的每个问题的upvote
列?这个子查询在普通MySQL中会是什么样子 - 在每个问题的每次迭代上运行一个额外的查询,并检查每个问题是否有数据记录,如果我作为当前用户对其投了赞成票/反对票
每次有人查看问题时计算选票会使问题花费越来越多的时间,因为问题需要扫描question_votes
表中的更多行。你应该希望它花同样的时间来看待一个全新的问题,就像一个已经被投票过的问题一样。
因此,您可以将列questions.score
与当前总数一起存储。
当用户投赞成票或反对票时,在question_votes
表中插入一行投票,这样就有了它的记录。如果你想允许用户更改他们的投票,这是必要的。
但同时,用户进行投票,递增或递减questions.score
值,并更新questions
表中的行。
理想情况下,如果这是完美的,questions.score
将是赞成票和反对票的总和。但也有可能由于代码错误,它不会一直都是完美的。事实上,假设它会变得稍微不准确。所以你可能需要一个";双重检查";后台任务,定期重新计算分数总和并固定分数。
这就是Stack Overflow的作用,如下所示:https://meta.stackexchange.com/questions/2677/database-schema-documentation-for-the-public-data-dump-and-sede
Posts
和Comments
表包括Score
列,即使个别投票存储在Votes
表的行中。
性能优势在于,当某些用户只是查看问题时,不需要从question_scores
表中读取任何行。查看问题的频率可能是投票的频率的许多倍,因此您可以通过存储投票总数来避免反复重新计算完全相同的总和值。
回复您的评论:
您必须查询question_votes
表,以了解当前用户是否对给定问题进行了投票。好消息是,每个问题最多需要检查一行,对吧?因此,如果您对表进行了索引以支持此查询,则无需检查许多行。
无论是在联接还是第二个查询中执行,它都非常轻量级,因为它只执行单行查找。
我只展示SQL,因为我不经常使用Laravel。
SELECT q.*, (v.user_id IS NOT NULL) AS i_voted_on_it
FROM questions q
LEFT OUTER JOIN question_votes v
ON v.question_id = q.id AND v.user_id = ?
如果您在question_votes(question_id, user_id)
上有一个索引,那么进行查找会更加高效。
如果有一行与条件匹配,包括user_id的固定值,那么它将返回一个非null结果。如果没有匹配的行,那么由于外部联接,它将为v
的所有列返回NULL。