Eloquent关联查询如何快速计算价格总和?withSum适配方案
Eloquent 关联条件累加优化方案
原实现存在N+1查询缺陷:首次查询拉取B模型集合后,遍历每条B数据都会单独发起一次关联查询,数据量上升后性能会明显下降,可根据业务场景选择以下两种更高效的实现:
方案1:单SQL直接聚合(性能最优)
如果不需要复用查询到的B模型集合,可直接通过子查询在数据库层完成累加计算,无需加载模型到内存、无循环逻辑,仅需一次数据库请求:
$total = B::where('user_id', $user) ->selectRaw('COALESCE(SUM(( SELECT counter FROM 关联表名 // 替换为b_by_a关联对应的实际数据表名 WHERE 关联表名.b_id = b.id // 替换为b_by_a关联定义的外键匹配规则 AND 你的筛选条件 // 对应传入的$condition,建议使用参数绑定避免SQL注入 LIMIT 1 )), 0) as total') ->value('total');
如果$condition是键值对数组格式的筛选条件,可以通过查询构造器的绑定方法安全传参,避免手动拼接SQL的风险。
方案2:带条件预加载(需复用B集合时使用)
如果后续逻辑需要使用查询到的B模型集合,可通过动态条件预加载关联数据,仅需2次数据库请求即可完成数据拉取,后续在集合层面完成累加,不会产生额外查询:
// 第一次查询:拉取符合条件的B集合 // 第二次查询:一次性拉取所有B模型匹配$condition的关联数据 $bCollection = B::where('user_id', $user) ->with(['b_by_a' => function ($query) use ($condition) { $query->where($condition)->select(['id', 'b_id', 'counter']); // 仅查询需要的字段,降低内存占用 }]) ->get(); // 集合内存中完成累加,无额外IO开销 $total = $bCollection->sum(function ($b) { return $b->b_by_a->first()?->counter ?? 0; });
关于withSum适配说明
你之前使用withSum无法适配业务,通常是没有传入闭包定义关联的动态筛选条件。如果你的业务需求是累加每个B模型下所有符合$condition的关联数据的counter总和(而非取第一条关联数据的counter),可以直接用带条件约束的withSum实现:
$total = B::where('user_id', $user) ->withSum(['b_by_a' => fn($q) => $q->where($condition)], 'counter') ->get() ->sum('b_by_a_sum_counter');
内容的提问来源于stack exchange,提问作者Zein
相关产品推荐
相关产品推荐

