laravel 10 中 eloquent 实现每日分组统计需用数据库日期函数(如 date())截断时间字段,配合 selectraw、groupbyraw 和聚合函数;须按 mysql/postgresql/sqlite 差异适配日期表达式,并推荐通过模型 scope 封装以保障跨库兼容性。

在 Laravel 10 中使用 Eloquent 获取「每日分组统计」结果,核心是利用数据库的日期函数(如 DATE())对时间字段做截断,再配合 groupBy() 和聚合函数(如 count()、sum())完成统计。关键在于适配不同数据库(MySQL、PostgreSQL、SQLite)的日期处理方式,同时保持 Eloquent 的可读性与链式调用习惯。
MySQL 下按日分组统计(最常用)
假设有一个 orders 表,含 created_at 字段,要统计每天的订单数:
$dailyStats = Order::selectRaw('DATE(created_at) as date, COUNT(*) as count')
->groupByRaw('DATE(created_at)')
->orderBy('date')
->get();
说明:
-
selectRaw和groupByRaw直接使用 MySQL 的DATE()函数提取日期部分(去掉时分秒) - 结果中
date是字符串格式(如"2024-06-15"),可直接用于图表或列表 - 注意:若需兼容时区,确保数据库和 PHP 的时区一致,或在查询前用
DB::raw("CONVERT_TZ(...)")调整
跨数据库兼容写法(推荐)
为避免硬写原生 SQL,Laravel 提供了 Illuminate\Support\Facades\DB 的日期函数辅助,但更稳妥的方式是用 cast: date + toDateString() 预处理——不过这仅适用于内存中处理。真正兼容的数据库层方案是:
- MySQL / MariaDB:用
DATE(created_at) - PostgreSQL:用
created_at::date或DATE(created_at) - SQLite:用
DATE(created_at)
实际项目中建议统一用 DB::raw() 并根据连接驱动微调,或封装一个 scope:
// 在 Order 模型中添加
public function scopeByDay($query, $column = 'created_at') {
$connection = DB::connection()->getDriverName();
$dateExpr = match($connection) {
'pgsql' => "$column::date",
default => "DATE($column)",
};
return $query->selectRaw("$dateExpr as date, COUNT(*) as count")
->groupByRaw($dateExpr)
->orderByRaw($dateExpr);
}
// 使用
$dailyStats = Order::byDay()->get();
带条件和多字段统计的扩展写法
例如:统计每天「已支付」订单数 + 总金额:
$stats = Order::selectRaw('DATE(created_at) as date')
->selectRaw('COUNT(*) as count')
->selectRaw('SUM(total_price) as total_amount')
->where('status', 'paid')
->groupByRaw('DATE(created_at)')
->orderBy('date')
->get();
注意点:
- 所有
selectRaw中的字段,只要不在聚合函数内,就必须出现在groupBy中(MySQL 严格模式下) - 如果想补全某几天(比如没订单的日子显示 0),Eloquent 本身不支持「左连接日历表」,需配合数据库日历表或应用层补零
- 大数据量时,确保
created_at字段有索引(如INDEX(created_at))提升分组效率
返回格式优化(转为数组或集合便于前端使用)
常见需求是转成以日期为 key 的数组:
$data = Order::selectRaw('DATE(created_at) as date, COUNT(*) as count')
->groupByRaw('DATE(created_at)')
->pluck('count', 'date')
->mapWithKeys(fn($count, $date) => [$date => (int)$count])
->all();
// 结果如:['2024-06-15' => 24, '2024-06-16' => 19, ...]
也可用 keyBy 保留对象结构:
$collection = Order::selectRaw('DATE(created_at) as date, COUNT(*) as count')
->groupByRaw('DATE(created_at)')
->get()
->keyBy('date');
不复杂但容易忽略的是时区和空值处理——记得检查 created_at 是否允许 NULL,必要时加 whereNotNull('created_at')。











