eloquent 按年分组统计需用 selectraw('year(created_at) as year') + groupby,避免 php 端处理;配合 sum()、orderby('year')、pluck 转标准图表结构,并注意索引优化与时区对齐。

用 Eloquent 按年分组统计数据
折线图的横轴是年份,纵轴是数值,核心是把原始数据按 year 聚合。Laravel 自带的 selectRaw + groupBy 就够用,不需要手写原生 SQL。
常见错误是直接用 created_at->year,但这是 PHP 端操作,无法在数据库层面分组,会导致全量查出再 PHP 循环——数据一多就卡死或内存溢出。
- MySQL 用
YEAR(created_at),PostgreSQL 用EXTRACT(YEAR FROM created_at) - Laravel 8+ 推荐统一用
selectRaw('YEAR(created_at) as year'),避免方言差异 - 记得加
orderBy('year'),否则前端画图时年份顺序乱 - 如果统计的是订单金额,别漏掉
sum('amount'),而不是只count(*)
$data = Order::selectRaw('YEAR(created_at) as year')
->selectRaw('SUM(amount) as total')
->whereBetween('created_at', ['2020-01-01', now()])
->groupBy('year')
->orderBy('year')
->get();
前端图表库怎么接 Laravel 返回的数据
后端返回的 $data 是 Collection,直接 json_encode 给前端就行,但要注意键名是否匹配图表库要求(比如 Chart.js 要 labels 和 datasets[0].data)。
容易踩的坑是没做 key 映射,比如后端字段叫 year 和 total,前端却硬写成 label 和 value,结果图表空白。
- 推荐在控制器里提前转成标准结构:
['labels' => $data->pluck('year'), 'data' => $data->pluck('total')] - 不要用
toArray()后再手动遍历——Collection 的pluck更安全、更少出错 - 如果年份有空缺(比如 2021 年没数据),Chart.js 不会自动补 0,得后端补全或前端用
Array.from({length: 5})对齐
性能瓶颈常出现在时间范围过大或关联查询上
当统计跨度超过 5 年、单表记录超百万时,GROUP BY YEAR(created_at) 可能走不了索引,尤其 created_at 没建索引或用了函数导致索引失效。
不是所有数据库都支持函数索引,MySQL 5.7+ 才支持函数索引,而且得显式创建:
- 先确认
created_at字段有索引:SHOW INDEX FROM orders WHERE Key_name = 'idx_created_at' - MySQL 8.0+ 可建函数索引:
CREATE INDEX idx_year ON orders (YEAR(created_at)) - 更稳妥的做法是加一个生成列
year并索引它,避免函数依赖 - 如果还连了
user表做统计(比如按用户注册年份+下单年份双维度),务必用withCount或子查询,别用join+groupBy,容易重复计数
为什么不用 Laravel Scout 或 Telescope 做这类统计
Scout 是搜索,Telescope 是调试,它们都不处理聚合计算。有人误以为 “Laravel 有现成报表包”,其实官方没提供开箱即用的统计图表组件——它只负责把数据干净地查出来。
真正省事的方式是:后端只管按年聚合、返回 JSON;前端用 Chart.js 或 ApexCharts 渲染。中间不加任何“报表中间件”或“统计服务类”,越简单越稳定。
复杂点在于年份对齐和时区——created_at 存的是 UTC,但业务要按本地年份统计(比如中国用户说的“2024 年”是东八区),得在查询前把时间范围转成 UTC,或者在数据库层用 CONVERT_TZ 处理。这点很容易被忽略,一到年底就发现 12 月数据少一半。










