如何在 Laravel 中合并两个查询并关联平均值与用户数据

12次阅读

如何在 Laravel 中合并两个查询并关联平均值与用户数据

本文介绍在 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 “让数据库做它擅长的事” 的设计哲学。

text=ZqhQzanResources