
本文介绍如何在 laravel 中基于原始 sql 实现按日期分组后的累计求和(cumulative sum),适用于统计类场景,如每日下载量的累加趋势分析。
本文介绍如何在 laravel 中基于原始 sql 实现按日期分组后的累计求和(cumulative sum),适用于统计类场景,如每日下载量的累加趋势分析。
在 Laravel 中,Eloquent ORM 本身不直接支持窗口函数(如 SUM() OVER (ORDER BY ...))或变量式累计计算(尤其在 MySQL 5.7 及以下版本),因此对已聚合的数据进行累计求和时,推荐使用原生 SQL 配合用户变量(MySQL)或现代窗口函数(MySQL 8.0+/PostgreSQL)来高效完成。
✅ 推荐方案:使用原生 SQL + 用户变量(兼容 MySQL 5.7)
以下代码在 Laravel 中安全执行累计求和查询,避免 PHP 层循环累加(提升性能、保证原子性):
use Illuminate\Support\Facades\DB;
$table = Downloads::getTableName();
$downloads = DB::select(<<<SQL
SELECT
date,
total_count,
@COUNTER := @COUNTER + total_count AS cumm_total_count
FROM (
SELECT
DATE(created_at) AS date,
COUNT(*) AS total_count
FROM $table
GROUP BY DATE(created_at)
ORDER BY date ASC
) AS tmp_data
CROSS JOIN (SELECT @COUNTER := 0) AS init_counter;
SQL
);⚠️ 注意事项:
MySQL8 CRM Schema Design Expert下载面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
- DATE(created_at) 比 DATE_FORMAT(created_at, '%Y-%m-%d') 更简洁且索引友好;
- 必须显式 ORDER BY date ASC(在子查询中),否则累计顺序不可靠;
- CROSS JOIN (SELECT @COUNTER := 0) 确保变量初始化,避免会话残留值干扰;
- 此写法适用于 MySQL;若使用 PostgreSQL 或 MySQL 8.0+,强烈推荐改用窗口函数(更标准、更安全):
✅ 进阶方案:MySQL 8.0+ / PostgreSQL — 使用窗口函数(推荐)
$downloads = DB::select(<<<SQL
SELECT
date,
total_count,
SUM(total_count) OVER (ORDER BY date ROWS UNBOUNDED PRECEDING) AS cumm_total_count
FROM (
SELECT
DATE(created_at) AS date,
COUNT(*) AS total_count
FROM {$table}
GROUP BY DATE(created_at)
) AS grouped
ORDER BY date;
SQL
);该写法语义清晰、无需变量、线程安全,且可被数据库优化器更好处理。
? 补充说明
- 不要在 PHP 中 foreach 累加:虽然可行(如 $sum += $item->total_count),但会丢失数据库层的计算优势,且在大数据量下易引发内存与性能问题;
- Eloquent 替代局限:withSum()、sum() 等聚合方法无法跨行累计,故不适用本场景;
- 结果结构:返回的是 stdClass 对象集合,可直接转为数组或 Collection:collect($downloads),便于后续视图渲染或 API 输出。
通过合理选择底层 SQL 策略,你可以在 Laravel 应用中稳健、高效地实现时间序列累计指标,为数据看板与业务分析提供可靠支撑。


















