Laravel 中合并两个查询:按日期关联课程平均值与用户明细数据

Laravel 中合并两个查询:按日期关联课程平均值与用户明细数据

本文介绍如何在 laravel 中将按日期分组的课程平均值查询与当前用户的明细记录进行左连接,生成包含平均值和用户实际值的联合结果集。

在 Laravel 开发中,常需将聚合统计(如某课程每日平均学习/辩论时长)与当前用户的原始行为数据对齐展示。直接使用 PHP 数组合并虽可行,但效率低、易出错,且无法利用数据库的优化能力。推荐方案是使用 Eloquent 或 Query Builder 的 LEFT JOIN 实现服务端精准关联

以下是以 date 字段为关联键,合并课程全局平均值与当前用户明细的完整实现:

✅ 推荐做法:单次数据库 JOIN 查询(高效、可读、可扩展)

use Illuminate/Support/Facades/DB;
use Illuminate/Support/Facades/Auth;

$result = DB::table('study_arguments as sa')
    ->select(
        'sa.date as avgDate',
        DB::raw('ROUND(AVG(sa.study), 1) as avgStudy'),
        DB::raw('ROUND(AVG(sa.argument), 1) as avgArgument'),
        'ua.study',
        'ua.argument'
    )
    ->join(DB::raw('(SELECT date, study, argument FROM study_arguments WHERE user_id = ?) as ua'), 'sa.date', '=', 'ua.date')
    ->where('sa.course_id', 2)
    ->groupBy('sa.date', 'ua.study', 'ua.argument')
    ->orderBy('sa.date')
    ->setBindings([$this->user()->id]) // 绑定用户 ID 防止 SQL 注入
    ->get();

? 说明: 使用子查询 (SELECT … WHERE user_id = ?) 构建用户专属数据视图 ua,确保仅关联该用户存在的日期; 主表 sa 按 course_id = 2 聚合计算每日平均值; JOIN 条件为 sa.date = ua.date,实现日期对齐; GROUP BY 必须包含所有非聚合字段(ua.study, ua.argument),否则 MySQL 8.0+ 会报错; setBindings() 安全注入用户 ID,避免硬编码导致的 SQL 注入风险。

⚠️ 注意事项

  • 若某日期在用户数据中存在、但在课程数据中无记录(如用户提交了未归属课程的数据),则该行不会出现在结果中。如需保留所有用户日期,应将 JOIN 改为 RIGHT JOIN 并调整主表;
  • AVG() 对 NULL 值自动忽略,但若 study/argument 全为 NULL,对应 AVG 返回 NULL,建议前端做空值处理(如 COALESCE(AVG(…), 0));
  • Laravel 9+ 中,可进一步封装为 Eloquent 关系或自定义查询作用域,提升复用性。

✅ 替代方案:Eloquent + Collection 合并(适合小数据量)

若因复杂逻辑难以写成单 SQL,也可在应用层合并:

VidAU

VidAU

VidAU AI 是一款AI驱动的数字人视频创作平台,旨在简化视频内容创作流程

下载

$avgData = StudyArgument::where('course_id', 2)
    ->selectRaw('date as avgDate, ROUND(AVG(study), 1) as avgStudy, ROUND(AVG(argument), 1) as avgArgument')
    ->groupBy('date')
    ->pluck(null, 'avgDate'); // 键为日期,值为对象

$userData = StudyArgument::where('user_id', Auth::id())
    ->select('date', 'study', 'argument')
    ->get()
    ->keyBy('date');

$merged = $userData->map(function ($item) use ($avgData) {
    $avg = $avgData->get($item->date);
    return $avg
        ? (object) array_merge((array)$avg, ['study' => $item->study, 'argument' => $item->argument])
        : null;
})->filter();

但此方式需两次查询 + 内存处理,不推荐用于大数据量或高并发场景

综上,优先采用原生 JOIN 方案——它语义清晰、性能优异,且完全符合关系型数据库的设计哲学。

https://www.php.cn/faq/1987402.html

发表回复

Your email address will not be published. Required fields are marked *