如何在 Laravel 中高效查询关联数据的统计值(计数与多列求和)

2026-05-30编程代码16366

本文介绍在 laravel 8+ 中,如何对一对多关系(如 buyer → orders)执行带时间条件的聚合查询,一次性获取关联记录数、指定列(如 price、charge)的条件求和,并确保查询高效且语法简洁。

本文介绍在 laravel 8+ 中,如何对一对多关系(如 buyer → orders)执行带时间条件的聚合查询,一次性获取关联记录数、指定列(如 price、charge)的条件求和,并确保查询高效且语法简洁。

在构建电商或订单管理系统时,常需从主模型(如 Buyer)出发,同时获取其关联子表(如 orders)的多项统计指标——不仅包括记录数量,还需对多个数值字段(如 price 和 charge)按相同业务条件(例如“近30天内”)分别求和。Laravel 8 引入的 withSum() 方法为此类需求提供了原生、优雅的解决方案。

✅ 正确用法:withCount() + 多次 withSum()

假设你已定义好 Buyer 模型的 orders 关系(hasMany(Order::class)),并有一个 $dateTo 时间变量(如 Carbon::now()->subDays(30)),可直接使用以下链式查询:

$buyers = Buyer::withCount([
    'orders' => fn ($query) => $query->where('created_at', '>=', $dateTo)
])
->withSum([
    'orders' => fn ($query) => $query->where('created_at', '>=', $dateTo)
], 'price')
->withSum([
    'orders' => fn ($query) => $query->where('created_at', '>=', $dateTo)
], 'charge')
->get();

执行后,每个 Buyer 实例将自动附加以下属性:

  • orders_count(满足时间条件的订单数)
  • orders_price_sum(该买家在指定时间段内所有订单的 price 总和)
  • orders_charge_sum(同时间段内所有订单的 charge 总和)

? 注意:withSum() 的第二个参数仅支持单列名(字符串),如需计算表达式(如 price + charge),需改用 selectRaw() + join() 或 DB::raw() 配合子查询,但会牺牲 Eloquent 的关系抽象优势。

Laravel 13.2.0

PHP中文网提供Laravel 13.2.0版本下载,Laravel框架 是基于 PHP 8.3+ 的高性能框架,官方推荐通过 Composer 安装。它内置 AI SDK、JSON:API Resources 及原生向量搜索,支持属性驱动开发与队列路由,大幅提升开发效率。相比旧版,13.2.0 优化了缓存 TTL 管理与实时通信,无需 Redis 即可横向扩展。作为现代 Web 开发首选,它兼顾安全与极速体验,助您快速构建企业级应用。

下载

⚠️ 关键注意事项

  • 版本要求:withSum() 仅在 Laravel 8.0+ 可用;若使用 Laravel 7 或更早版本,需通过 selectSub() 手动构造子查询,或升级框架。
  • 闭包变量传递:在箭头函数(fn)中引用外部变量(如 $dateTo)无需显式 use(),但若使用传统匿名函数(function ($query)),必须写成 function ($query) use ($dateTo)。
  • 性能优化:上述写法会在单条 SQL 中生成多个关联子查询(SELECT ... (SELECT SUM(...) FROM ...) AS ...),避免 N+1 查询,比循环调用 sum() 更高效。
  • 空值处理:当某买家无符合条件的订单时,*_sum 字段将返回 null,建议在视图或 API 响应中做空值转换(如 ?? 0)。

✅ 推荐进阶实践(Laravel 9+)

若需复用同一时间范围逻辑,可将条件封装为本地作用域(Scope)提升可维护性:

// In Buyer model
public function scopeWithRecentOrderStats($query, $dateFrom)
{
    return $query
        ->withCount(['orders' => fn ($q) => $q->where('created_at', '>=', $dateFrom)])
        ->withSum(['orders' => fn ($q) => $q->where('created_at', '>=', $dateFrom)], 'price')
        ->withSum(['orders' => fn ($q) => $q->where('created_at', '>=', $dateFrom)], 'charge');
}

// Usage:
$buyers = Buyer::withRecentOrderStats($dateTo)->get();

综上,结合 withCount() 与多次 withSum() 是 Laravel 8+ 中实现“带条件多列关联聚合”的标准、推荐方案——语义清晰、性能可控、代码简洁,是现代 Laravel 应用中处理统计类报表查询的最佳实践之一。

本文地址:https://www.ufcn.cn/article/2403591.html

如非特殊说明,本站内容均来自于网友自主分享,概不代表本站观点,如有任何问题我们都将在收到反馈后的第一时间进行处理!