Laravel关联数据过滤:用户当日最短游戏记录去重查询求助
解决方案:数据库层面实现当日用户最短游戏时长排行榜
不需要用foreach遍历处理,完全可以通过数据库查询直接实现需求,效率更高,尤其是数据量较大时。以下提供两种适配Laravel Eloquent的实现方案:
方案一:仅获取用户信息+最短时长(高效简洁)
适合只需要展示用户和其最短游戏时长的场景,通过JOIN+聚合函数直接在数据库层面分组计算:
对应SQL语句
SELECT l.*, MIN(p.time) AS shortest_time FROM leads l JOIN plays p ON l.id = p.lead_id WHERE DATE(p.played_at) = CURDATE() GROUP BY l.id ORDER BY shortest_time ASC;
Laravel Eloquent代码实现
$today = Carbon::today(); $data = Lead::select('leads.*', DB::raw('MIN(plays.time) as shortest_time')) ->join('plays', 'leads.id', '=', 'plays.lead_id') ->whereDate('plays.played_at', '=', $today) ->groupBy('leads.id') ->orderBy('shortest_time', 'asc') ->get();
注意:如果MySQL开启了ONLY_FULL_GROUP_BY模式,需要确保SELECT中的非聚合字段都在GROUP BY中,或者调整sql_mode。但由于leads.id是主键,大多数情况下分组查询主键可以直接获取用户的其他字段。
方案二:获取用户+对应最短时长的完整游戏记录
如果需要展示那条最短时长游戏记录的其他字段(比如游戏类型、完成状态等),可以通过子查询关联找到对应记录:
对应SQL语句
SELECT l.*, p.* FROM leads l JOIN plays p ON l.id = p.lead_id WHERE DATE(p.played_at) = CURDATE() AND p.time = ( SELECT MIN(time) FROM plays WHERE lead_id = l.id AND DATE(played_at) = CURDATE() ) ORDER BY p.time ASC;
Laravel Eloquent代码实现
$today = Carbon::today(); // 子查询获取每个用户当日的最短游戏时长 $minTimeSubquery = DB::table('plays') ->select('lead_id', DB::raw('MIN(time) as min_time')) ->whereDate('played_at', '=', $today) ->groupBy('lead_id'); $data = Lead::select('leads.*', 'plays.*') ->join('plays', function($join) use ($minTimeSubquery, $today) { $join->on('leads.id', '=', 'plays.lead_id') ->whereDate('plays.played_at', '=', $today) ->joinSub($minTimeSubquery, 'min_plays', function($join) { $join->on('plays.lead_id', '=', 'min_plays.lead_id') ->on('plays.time', '=', 'min_plays.min_time'); }); }) ->orderBy('plays.time', 'asc') ->get();
为什么不推荐原代码的方式?
你当前的代码会查询出每个用户当日的所有游戏记录,之后需要在PHP层面遍历筛选最短记录,这种方式在用户量和记录数较多时,会占用大量内存,且效率远低于数据库层面的计算。
内容的提问来源于stack exchange,提问作者gobs
相关产品推荐
相关产品推荐

