Laravel 11中统计近30天每日数据量需结合Eloquent日期查询与分组聚合,基础用DATE(created_at)分组计数,完整补零则需PHP生成连续日期并合并查询结果,确保含0值。

在 Laravel 11 中统计近 30 天每日数据量,核心是利用 Eloquent 的日期查询 + 分组聚合,配合数据库的日期函数(如 MySQL 的 DATE())实现按天分组计数。关键点在于正确截取日期、处理时区、避免全表扫描,并确保结果包含连续的 30 天(即使某天无数据也要显示为 0)。
基础统计:有数据的日期直接分组计数
适用于快速查看哪些天有记录,不要求补零:
$dailyCounts = YourModel::selectRaw('DATE(created_at) as date, COUNT(*) as count')
->where('created_at', '>=', now()->subDays(29)) // 从今天起往前推 29 天,共 30 天
->groupBy('date')
->orderBy('date')
->get();
说明:
- 使用 DATE(created_at) 剥离时间部分,只保留年月日;
- now()->subDays(29) 确保覆盖“今天”到“30 天前”,共 30 个自然日;
- 注意时区:确保 created_at 存储和查询使用一致时区(推荐统一用 UTC,应用层转换展示)。
完整补零:强制返回连续 30 天(含 0 计数)
Laravel 本身不支持 SQL 的「右连接日期序列」,需结合 PHP 补全空缺日期:
- 先查出数据库中实际存在的每日计数(同上),转为以日期为 key 的数组;
- 用 PHP 生成近 30 天的日期列表(从今天倒推);
- 遍历日期列表,从查询结果中取值,不存在则设为 0。
// 查询已有数据
$rawData = YourModel::selectRaw('DATE(created_at) as date, COUNT(*) as count')
->whereBetween('created_at', [now()->subDays(29), now()])
->groupBy('date')
->pluck('count', 'date'); // ['2024-05-01' => 12, '2024-05-03' => 7]
<p>// 生成 30 天日期范围(含今天)
$dates = collect(range(0, 29))
->map(fn($i) => now()->subDays($i)->toDateString())
->reverse(); // 从 30 天前 → 今天</p><p>// 补零合并
$dailyCounts = $dates->mapWithKeys(function ($date) use ($rawData) {
return [$date => $rawData->get($date, 0)];
})->values();
优化建议与注意事项
-
索引必须加:确保
created_at字段有 B-tree 索引,否则大表下性能极差; -
避免 SELECT *:只查需要字段(
DATE()和COUNT()),减少传输开销; -
考虑数据量大时用缓存:例如用
Cache::remember('daily_counts_30d', 3600, fn() => ...)缓存 1 小时; - 注意软删除:如果模型启用软删除,Laravel 默认自动排除已删除记录,无需额外处理;
-
跨月/跨年无影响:
DATE()和 PHP 的toDateString()天然支持任意日期范围。
扩展:按小时或周统计只需微调
想改成近 7 天每小时统计?把 DATE(created_at) 换成 DATE_FORMAT(created_at, "%Y-%m-%d %H:00"),时间范围用 subHours(167)(7×24−1),其余逻辑不变。周统计同理,用 YEARWEEK(created_at, 1) 或 PHP 的 format('o-W')。


















