
本文介绍在 laravel 中高效统计指定时间范围内(日/周/月)文件下载次数的方法,使用数据库原生聚合避免内存遍历,支持补全无数据日期并返回标准格式的键值对结果。
本文介绍在 laravel 中高效统计指定时间范围内(日/周/月)文件下载次数的方法,使用数据库原生聚合避免内存遍历,支持补全无数据日期并返回标准格式的键值对结果。
在 Laravel 应用中,当需要按时间维度(如天、周、月)统计模型关联记录(例如 File → Download)的发生频次时,切忌先加载全部数据再用 PHP 分组计数——这会导致性能急剧下降,尤其在下载量较大时。正确做法是将分组与聚合下推至数据库层,并辅以日期补全逻辑,确保结果既准确又完整。
✅ 推荐方案:数据库聚合 + 日期区间补全
以下示例以「按天统计指定日期范围内下载次数」为例,返回形如 ['2022-09-02' => 1, '2022-09-03' => 2] 的数组:
Laravel Cloud(cloud.laravel.com)。此技能用于处理所有 Laravel Cloud 请求,包括搜索和读取数据。只要任务涉及 Laravel Cloud,即使用此技能。
use Illuminate\Support\Facades\DB;
use Carbon\CarbonPeriod;
// 1. 定义时间范围(支持动态传参)
$startDate = '2022-09-01';
$endDate = '2022-09-05';
// 2. 数据库聚合查询:按日期分组计数(使用标准 Y-m-d 格式)
$downloadsByDate = DB::table('downloads')
->select(
DB::raw('COUNT(*) as total'),
DB::raw("DATE(created_at) as date") // MySQL 原生 DATE() 提取日期部分,简洁可靠
)
->whereBetween('created_at', [$startDate, $endDate])
->groupBy('date')
->get()
->keyBy('date'); // 转为以 '2022-09-02' 为 key 的集合,便于快速查找
// 3. 生成完整日期序列(含无下载记录的日期)
$period = CarbonPeriod::create($startDate, $endDate);
$result = [];
foreach ($period as $date) {
$formattedDate = $date->format('Y-m-d');
$result[$formattedDate] = $downloadsByDate->get($formattedDate)?->total ?? 0;
}
// 输出示例:
// [
// '2022-09-01' => 0,
// '2022-09-02' => 1,
// '2022-09-03' => 2,
// '2022-09-04' => 0,
// '2022-09-05' => 3,
// ]? 扩展支持周/月统计
-
按周统计:将
DATE(created_at)替换为YEARWEEK(created_at, 1)(MySQL),并相应调整分组与格式化逻辑; -
按月统计:使用
DATE_FORMAT(created_at, '%Y-%m')或YEAR(created_at), MONTH(created_at);
? 提示:若需关联特定文件(如
$file->downloads),可添加where('file_id', $file->id)条件;但注意——避免调用$file->downloads->groupBy(...)等 Eloquent 集合方法,因其会强制加载全部记录到内存,违背性能优化初衷。
⚠️ 注意事项
-
DATE(created_at)在 MySQL 中自动截断时间为00:00:00,语义清晰且索引友好(需确保created_at字段有索引); - 使用
CarbonPeriod可确保结果包含所有目标日期,即使某日无下载记录也返回0,避免前端处理空缺逻辑; - 若使用 PostgreSQL,将
DATE(created_at)替换为created_at::date; - 时间范围建议使用
whereBetween()并传入 Carbon 实例(如now()->subWeek()),提升可读性与时区安全性。
该方案兼顾性能、可读性与健壮性,是 Laravel 生态中处理时间维度聚合统计的推荐实践。

















