Laravel中执行groupBy语句时如何获取关联关系集合
实现方案
你可以根据业务数据量选择对应的实现方式,所有方案均基于现有表结构和Eloquent查询逻辑扩展:
方案1:全量预加载+内存归集(适合中小数据量场景,代码可读性最高)
首先在BuyerZip模型中定义和买家表的关联:
// app/Models/BuyerZip.php public function buyer() { return $this->belongsTo(Buyer::class, 'buyer_id', 'id'); }
查询逻辑调整为预加载关联后按zip_id归集即可,同时保留需要的分组统计数:
$zipResult = BuyerZip::with('buyer') ->get() ->groupBy('zip_id') ->map(function ($relationRecords, $zipId) { return [ 'zip_id' => $zipId, 'count' => $relationRecords->count(), // 对应原逻辑的分组记录总数 // 去重处理:避免同一个买家绑定多条zip关联记录时重复返回 'buyers' => $relationRecords->pluck('buyer')->unique('id')->values() ]; })->values();
该方案不需要编写复杂原生SQL,逻辑直观,单表数据量在10w条以内时性能足够。
方案2:分块查询合并(适合大数据量场景,内存占用更低)
如果buyer_zips表数据量较大,一次性查全表会占用过高内存,可以拆分查询步骤:
// 1. 保留原有分组统计逻辑,结果按zip_id做键方便后续赋值 $zipStats = BuyerZip::selectRaw('zip_id, count(*) as count') ->groupBy('zip_id') ->get() ->keyBy('zip_id'); // 2. 批量查询所有涉及zip对应的关联买家数据 $zipBuyerMap = BuyerZip::with('buyer') ->whereIn('zip_id', $zipStats->keys()) ->get() ->groupBy('zip_id') ->map(fn($records) => $records->pluck('buyer')->unique('id')->values()); // 3. 把买家数据合并到统计结果中 foreach ($zipStats as $zipId => $statItem) { $statItem->buyers = $zipBuyerMap->get($zipId, collect()); }
最终$zipStats就是目标结果,每个条目包含zip_id、关联记录总数、对应所有买家数据。
方案3:SQL聚合函数直出(适合对查询性能要求极高的场景)
如果使用MySQL数据库,可以借助GROUP_CONCAT聚合函数在分组时直接拿到关联的买家id,减少查询次数:
// 分组时同时聚合去重后的buyer_id $zipStats = BuyerZip::selectRaw('zip_id, count(*) as count, GROUP_CONCAT(DISTINCT buyer_id) as raw_buyer_ids') ->groupBy('zip_id') ->get(); // 批量查询所有关联的买家实体 $allBuyers = Buyer::whereIn( 'id', $zipStats->pluck('raw_buyer_ids')->flatMap(fn($idStr) => explode(',', $idStr)) )->get()->keyBy('id'); // 挂载买家数据,清理临时字段 $zipStats->transform(function ($item) use ($allBuyers) { $buyerIdList = explode(',', $item->raw_buyer_ids); $item->buyers = $allBuyers->only($buyerIdList)->values(); unset($item->raw_buyer_ids); return $item; });
注意:MySQL默认
GROUP_CONCAT最大长度为1024字节,如果单个zip下关联的买家数量较多,会出现id被截断的问题,需要提前调大group_concat_max_len数据库配置,生产环境优先选择前两种方案稳定性更高。
内容的提问来源于stack exchange,提问作者Axel
相关产品推荐
相关产品推荐

