Laravel统计每篇文章评论的去重用户数最优实现方法
需求说明
- 业务涉及
Post(文章)、comments(评论表)、用户三类数据:每篇文章关联多条评论,每条评论归属于单个用户。 - 目标:查询文章列表时,同步统计每篇文章下发表过评论的独立去重用户总数,同一用户对同一篇文章发表多条评论仅计数1次。
- 当前实现存在性能问题,代码如下:
$posts = Post::query() ->where('category',$request->input('category')) // other conditions ->get(); $numberOfUsers=[]; forech($posts as $post){ $numberOfUsers[$post->id] = $post->comments()->groupBy('user')->count(); }
- 此前尝试使用
belongsToMany、hasMany、hasManyThrough等关联方法搭配groupBy查询,仅能得到评论总条数,无法得到去重用户数,需要正确的高效实现方案。
高效实现方案
方案1:模型定义去重关联+预加载统计(推荐)
在Post模型中定义评论用户的多对多关联,自动完成用户去重:
// Post模型内添加关联方法 public function commentUsers() { return $this->belongsToMany(User::class, 'comments', 'post_id', 'user_id') ->distinct(); }
查询时使用withCount批量预加载统计值,全程仅执行2条SQL,完全避免N+1查询问题:
$posts = Post::query() ->where('category', $request->input('category')) // 其他筛选条件 ->withCount('commentUsers') ->get();
统计结果会自动挂载到每个文章模型的comment_users_count属性上,直接调用即可:
foreach ($posts as $post) { // $post->comment_users_count 就是当前文章的去重评论用户数 }
方案2:闭包统计(无需新增模型关联)
如果不想新增模型关联方法,可以直接在withCount中传入闭包,手动写去重统计逻辑:
$posts = Post::query() ->where('category', $request->input('category')) ->withCount([ 'comments' => function ($q) { $q->select(\DB::raw('count(distinct user_id)')); } ]) ->get();
统计结果挂载在comments_count属性上,直接访问即可。
原循环查询写法属于典型N+1问题:每遍历一篇文章就执行1次count查询,文章总量越大查询次数越多,性能会随数据量增长线性下降。上述两种方案均为批量查询,性能差距可达数十到数百倍。
内容的提问来源于stack exchange,提问作者Pejman
相关产品推荐
相关产品推荐

