如何在数据库中批量统计10个活动的男女童访客数避免N+1问题
问题描述
现有Laravel查询代码:
DB::table('visitors') ->join('event_visitor', 'visitors.id', '=', 'event_visitor.visitor_id') ->where('sex', 0) ->where('event_visitor.event_id', 1) ->count();
该代码用于统计活动ID为1的男性访客数量。
现需统计10个活动中男性、女性、儿童的访客数,最终要得到如下格式的数组:
$men = [100, 200, 300 ,400,500,600,700,800,900,1000]; $women = [100, 200, 300 ,400,500,600,700,800,900,1000]; $kids = [100, 200, 300 ,400,500,600,700,800,900,1000];
其中sex字段对应关系:
- 0 = 男性(men)
- 1 = 女性(women)
- 2 = 儿童(kids)
要求在数据库层面实现,同时避免N+1查询问题。
解决方案
可以通过一次聚合查询,利用条件统计逻辑在数据库层面完成所有活动的分类统计,彻底规避N+1查询问题。
1. 编写聚合查询
使用Laravel查询构造器,通过CASE WHEN实现条件统计,按活动ID分组获取数据:
// 替换为实际需要统计的10个活动ID列表 $targetEventIds = [1,2,3,4,5,6,7,8,9,10]; $eventStats = DB::table('visitors') ->join('event_visitor', 'visitors.id', '=', 'event_visitor.visitor_id') ->whereIn('event_visitor.event_id', $targetEventIds) ->selectRaw(' event_visitor.event_id, COUNT(CASE WHEN visitors.sex = 0 THEN 1 END) as men_count, COUNT(CASE WHEN visitors.sex = 1 THEN 1 END) as women_count, COUNT(CASE WHEN visitors.sex = 2 THEN 1 END) as kids_count ') ->groupBy('event_visitor.event_id') ->orderBy('event_visitor.event_id') ->get();
2. 转换为目标数组格式
将查询结果整理成要求的三个数组,保持活动ID的顺序一致性:
$men = []; $women = []; $kids = []; foreach ($eventStats as $stat) { $men[] = $stat->men_count; $women[] = $stat->women_count; $kids[] = $stat->kids_count; }
关键说明
- 避免N+1:全程仅执行1次数据库查询,所有统计逻辑在数据库端完成,无需循环每个活动单独发起请求。
- 条件统计原理:
CASE WHEN会根据sex值判断是否计入统计,COUNT函数会自动忽略NULL值(不满足条件时CASE返回NULL),从而精准得到对应类别的访客数量。 - 效率优化:
whereIn限定了目标活动范围,避免查询无关数据;orderBy确保结果按活动ID排序,保证最终数组顺序与目标活动列表一致。
内容的提问来源于stack exchange,提问作者Abdoullah al-hajj
相关产品推荐
相关产品推荐

