
本文详解如何利用 laravel 的查询构建器和 carbon 库,从 exercises 表的日期字段中提取月份编号(如 1–12),实现按月聚合统计用户训练天数,并找出训练最频繁的月份。
本文详解如何利用 laravel 的查询构建器和 carbon 库,从 exercises 表的日期字段中提取月份编号(如 1–12),实现按月聚合统计用户训练天数,并找出训练最频繁的月份。
在 Laravel 开发中,常需对日期型字段(如 day)进行时间维度聚合分析。你已成功使用 whereMonth() 和 whereYear() 获取当前月的训练天数,但要找出“历史中训练天数最多的月份”,关键在于从存储的日期中动态提取月份信息并分组统计,而非依赖额外表。
Laravel 的查询构建器原生支持 groupByRaw() 配合 MySQL 的 MONTH() 函数,无需手动解析日期字符串。推荐写法如下:
use Illuminate\Support\Facades\DB;
$topMonth = DB::table('exercises')
->selectRaw('MONTH(day) as month_num, COUNT(DISTINCT DATE(day)) as workout_days')
->where('user_id', auth()->id())
->groupByRaw('MONTH(day)')
->orderByDesc('workout_days')
->limit(1)
->first();
if ($topMonth) {
echo "训练最多的月份是:{$topMonth->month_num}月(共{$topMonth->workout_days}天)";
}
✅ 说明与优势:
- MONTH(day) 直接由数据库执行,高效且跨时区安全;
- COUNT(DISTINCT DATE(day)) 确保同一天多次训练只计 1 天(符合“训练日”语义);
- groupByRaw('MONTH(day)') 按月份分组,避免 PHP 层循环处理;
- 结果直接返回最高频次的月份编号(如 6 表示六月),便于前端展示或后续逻辑使用。
⚠️ 注意事项:
- 若需兼容 PostgreSQL 或 SQLite,应改用 EXTRACT(MONTH FROM day) 或 strftime('%m', day);
- Carbon 解析(如 Carbon::parse($date)->month)适用于单条日期处理,不适用于数据库聚合场景——它会将全部数据拉到 PHP 内存中,严重降低性能;
- 确保 day 字段为 DATE 或 DATETIME 类型,且索引合理(例如联合索引 (user_id, day) 可大幅提升查询效率)。
总结:提取月份的核心在于善用数据库原生函数完成聚合,而非在应用层逐条解析。结合 groupByRaw 与 MONTH(),即可简洁、高效地实现“月度训练热度分析”,并轻松扩展为年度对比、趋势图表等高级功能。











