Laravel查询用户挑战进度如何实现升序排序及分页且不使用foreach循环
性能优化方案:查询层实现进度计算、排序与分页
你现有代码的核心性能问题是全量拉取数据后双层循环计算,同时存在N+1查询,完全无法支撑大数据量场景,下面提供两种可直接实现排序+分页的无foreach方案:
方案1:SQL层直接聚合计算(适合逻辑不复杂的场景)
将missionState方法中统计have_to_mission_count和cert_count的逻辑转化为SQL聚合条件,一次性完成进度计算、排序、分页:
$perPage = 10; // 自定义每页展示条数 $users = \App\Models\ChallengeUserPivot::where('challenge_id', $challenge_id) // 关联用户表避免N+1查询 ->join('users', 'challenge_user_pivots.user_id', '=', 'users.id') // 关联当前挑战的所有任务 ->join('challenge_missions', 'challenge_missions.challenge_id', '=', 'challenge_user_pivots.challenge_id') // 左连用户任务完成记录表,统计已完成任务数,条件根据你实际业务调整 ->leftJoin('user_mission_certs', function ($join) { $join->on('user_mission_certs.user_id', '=', 'users.id') ->on('user_mission_certs.mission_id', '=', 'challenge_missions.id') ->where('user_mission_certs.is_valid', 1); }) // 聚合计算总任务数、已完成数、进度值 ->selectRaw(' challenge_user_pivots.*, users.*, COUNT(challenge_missions.id) as total_mission, COUNT(user_mission_certs.id) as completed_mission, (COUNT(user_mission_certs.id) / COUNT(challenge_missions.id)) * 100 as progress ') ->groupBy('challenge_user_pivots.user_id') // 按进度升序排序 ->orderBy('progress', 'asc') // 直接调用Laravel分页方法实现分页 ->paginate($perPage);
该方案优势:
- 全程仅执行1次SQL查询,无PHP层循环逻辑,性能不受用户量级增长影响
- 进度计算、排序逻辑全部下沉到数据库层,无需额外遍历处理结果集
- 直接复用Laravel原生分页器,自带分页所需的总条数、上一页/下一页标识等参数
方案2:冗余字段预存进度(适合超大数据量、逻辑复杂的场景)
如果missionState的业务逻辑非常复杂,无法转化为SQL条件,就用冗余存储方案:
- 给
challenge_user_pivots表新增progress(decimal类型)冗余字段 - 在用户完成任务、挑战任务配置变更时,通过事件、观察者或者队列异步更新对应用户的progress值
- 查询时直接基于冗余字段排序分页,性能达到最优:
$perPage = 10; $users = \App\Models\ChallengeUserPivot::where('challenge_id', $challenge_id) ->with('user') // 预加载用户信息避免N+1 ->orderBy('progress', 'asc') ->paginate($perPage);
内容的提问来源于stack exchange,提问作者madol
相关产品推荐
相关产品推荐

