
本文介绍在 laravel 中通过 sql join 或集合合并方式,将课程统计的每日平均值(avgstudy/avgargument)与当前用户的实际学习数据(study/argument)按日期对齐,生成结构化结果集。
在 laravel 中实现两个查询结果按 date 字段精准合并(即仅保留两个结果集中都存在的日期),推荐使用 数据库层面的 INNER JOIN,而非 php 层面的集合合并——这样更高效、语义更清晰,且能充分利用数据库索引与优化器。
✅ 推荐方案:单次 SQL JOIN 查询(最优实践)
直接在数据库中完成聚合与关联,避免 N+1 和内存遍历:
use IlluminateSupportFacadesDB; use IlluminateSupportFacadesAuth; $result = DB::table(DB::raw('( SELECT date AS avgDate, ROUND(AVG(study), 1) AS avgStudy, ROUND(AVG(argument), 1) AS avgArgument FROM study_arguments WHERE course_id = 2 GROUP BY date ) AS avg_data')) ->join(DB::raw('( SELECT date, study, argument FROM study_arguments WHERE user_id = ? ) AS user_data'), 'avg_data.avgDate', '=', 'user_data.date') ->select( 'avg_data.avgDate', 'avg_data.avgStudy', 'avg_data.avgArgument', 'user_data.study', 'user_data.argument' ) ->setBindings([Auth::user()->id]) ->get();
? 说明: 外层 DB::table(…) 包裹子查询 avg_data(课程 2 的每日均值); join(…) 关联 user_data 子查询(当前用户所有记录); ON avg_data.avgDate = user_data.date 确保只返回双方共有的日期(即你期望的三行结果); setBindings() 安全绑定用户 ID,防止 SQL 注入。
⚠️ 注意事项
- 日期类型一致性:确保 study_arguments.date 字段为 DATE 类型(非 DATETIME),否则 GROUP BY date 和 JOIN 可能因时间部分不匹配而失败。建表时应使用 $table->date(‘date’)(如答案中所示)。
- 空值处理:若某日用户有记录但课程无其他用户数据(导致 AVG 为 NULL),该行将被 INNER JOIN 自动排除;如需保留用户数据(显示 NULL 平均值),请改用 LEFT JOIN 并调整子查询逻辑。
- 性能优化:为 study_arguments(course_id, date) 和 (user_id, date) 添加复合索引:
// 在 migration 中添加 $table->index(['course_id', 'date']); $table->index(['user_id', 'date']);
? 替代方案:Eloquent + 集合合并(适用于简单场景)
若因复杂条件难以写 JOIN,可先获取两组数据,再用 Laravel Collection 合并:
$avgData = StudyArgument::where('course_id', 2) ->selectRaw('date as avgDate, ROUND(AVG(study), 1) as avgStudy, ROUND(AVG(argument), 1) as avgArgument') ->groupBy('date') ->get() ->keyBy('avgDate'); // 以 date 为键 $userData = StudyArgument::where('user_id', Auth::user()->id) ->select('date', 'study', 'argument') ->get() ->keyBy('date'); // 合并:只取交集日期 $merged = $avgData->intersectByKeys($userData)->map(function ($avg, $date) use ($userData) { return (object) [ 'avgDate' => $date, 'avgStudy' => $avg->avgStudy, 'avgArgument' => $avg->avgArgument, 'study' => $userData[$date]->study, 'argument' => $userData[$date]->argument, ]; })->values();
✅ 优点:逻辑直观,便于调试;
❌ 缺点:两次查询 + 内存处理,大数据量时性能较差,且无法利用数据库 JOIN 优化。
✅ 总结
| 方案 | 适用场景 | 效率 | 推荐度 |
|---|---|---|---|
| 原生 JOIN 子查询 | 生产环境、数据量大、需精确关联 | ⭐⭐⭐⭐⭐ | ★★★★★ |
| Collection 合并 | 快速原型、逻辑复杂难 SQL 化、数据量小 | ⭐⭐ | ★★☆ |
始终优先选择数据库层关联——它更可靠、更快速,也更符合 Laravel “让数据库做它擅长的事” 的设计哲学。