
本文详解如何利用 Laravel 集合(Collection)与嵌套 sum() 方法,对 PostgreSQL 的 JSONB 字段(如 data->body->aaa 等)中多个数值键进行动态、可扩展的聚合求和,并安全写入汇总表。
本文详解如何利用 laravel 集合(collection)与嵌套 `sum()` 方法,对 postgresql 的 jsonb 字段(如 `data->body->aaa` 等)中多个数值键进行动态、可扩展的聚合求和,并安全写入汇总表。
在 Laravel 应用中,当业务需要将高频采集的结构化 JSONB 数据(如每月 4 条客户指标记录)按客户维度聚合为月度汇总时,直接使用 SQL 的 SUM() 配合 ->> 操作符虽可行,但面对多键(aaa, bbb, ccc, ddd)且需灵活扩展的场景,纯数据库层处理易冗余、难维护。更优雅的方式是借助 Laravel Collection 的函数式能力,在应用层完成精准、可读性强的聚合。
✅ 正确做法:分层聚合 + 安全类型转换
假设你的原始数据模型 SummaryReport 表结构如下:
- customer_id: BIGINT
- data: JSONB(存储形如 {"body": {"aaa": 22, "bbb": 22, ...}} 的对象)
以下代码实现按客户 ID 聚合所有 body 下全部数值字段之和:
use Illuminate\Support\Collect;
// 1. 按 customer_id 分组原始记录(推荐:先查出目标客户批次,避免全表加载)
$reports = SummaryReport::whereBetween('created_at', [$startOfMonth, $endOfMonth])
->get();
// 2. 构建客户维度汇总数组
$aggregated = $reports->groupBy('customer_id')->map(function ($group, $customerId) {
// 提取每个记录中 data->body 的所有数值,并求和(自动跳过非数字值)
$total = $group->sum(function ($report) {
$body = data_get(json_decode($report->data, true), 'body', []);
return collect($body)->filter(fn($v) => is_numeric($v))->sum();
});
return [
'customer_id' => (int) $customerId,
'monthly_sum' => (int) $total,
'created_at' => now(),
];
})->values();
// 3. 批量插入汇总表(如 MonthlySummary)
MonthlySummary::upsert($aggregated->all(), ['customer_id'], ['monthly_sum', 'updated_at']);? 关键点说明:
- data_get(..., 'body', []) 比 json_decode(...)->body 更健壮,避免因字段缺失导致的 Trying to get property 'body' of non-object 错误;
- collect($body)->filter(...)->sum() 确保仅对合法数字求和,兼容 null、字符串数字(如 "22")或意外空值;
- 使用 upsert() 替代 insert(),支持存在则更新、不存在则插入,天然适配月度任务重跑场景。
⚠️ 注意事项与最佳实践
- 性能提示:若单月记录达万级,避免 get() 全量加载。改用 cursorPaginate() 或原生查询 + jsonb_each_text() 预聚合(适用于仅需总和、无需 PHP 处理逻辑的场景);
-
JSONB 键名硬编码风险:如需分别统计 aaa/bbb 等各字段,可将键列表抽象为配置:
$keys = ['aaa', 'bbb', 'ccc', 'ddd']; $perKeySum = collect($body)->only($keys)->filter(...)->sum();
- 事务保障:将汇总逻辑包裹在 DB::transaction() 中,确保“读取 → 计算 → 写入”原子性;
- 空值防御:始终对 json_decode() 结果做 null 判断,或使用 json_decode($str, true) ?? [] 避免对象访问异常。
通过以上方式,你不仅解决了 JSONB 多键求和的技术难点,更构建了一套可测试、可监控、易于横向扩展的聚合流程——这正是现代 Laravel 数据工程实践的核心要义。










