Laravel分组查询获取按供应商拆分的库存数据是否需要使用子查询
不需要完全重写现有查询,有两种实现方案,可根据业务场景选择,优先推荐ORM关联方案,可读性和可维护性更高。
方案一:Eloquent ORM 关联+集合后处理(推荐)
利用Laravel自带的关联预加载和集合操作实现,不需要调整复杂的SQL逻辑:
// 先确保StockPart模型已定义stockItems关联 class StockPart extends Model { public function stockItems() { return $this->hasMany(StockItem::class); } } // 查询代码 $stockParts = StockPart::query() ->select(['id', 'title']) // 原有总库存统计 ->withCount([ 'stockItems as available' => function ($query) use ($stockId) { $query->where('current_stock_id', $stockId); } ]) // 原有总价值统计 ->withSum([ 'stockItems.shipment as totalValue' ], 'item_cost') // 预加载关联数据用于供应商维度统计 ->with(['stockItems' => function ($query) use ($stockId) { $query->where('current_stock_id', $stockId) ->select('id', 'stock_part_id', 'shipment_id') ->with('shipment:id,supplier_id,item_cost'); }]) ->orderBy('title') ->get() // 后处理生成按供应商分组的统计 ->map(function (StockPart $stockPart) { $stockPart->available_by_seller = $stockPart->stockItems ->groupBy('shipment.supplier_id') ->map(function ($items, int $supplierId) { return [ 'supplier_id' => $supplierId, 'available' => $items->count(), 'totalValue' => $items->sum('shipment.item_cost') ]; }) ->values(); // 清理不需要的原始关联字段 unset($stockPart->stockItems); return $stockPart; }) ->toArray();
该方案适合大多数中小数据量场景,代码逻辑清晰,后续拓展其他统计维度成本低
方案二:原生SQL聚合(适合大数据量场景)
如果数据量较大,不想在PHP层面做后处理,可以修改现有查询,利用MySQL 5.7+支持的JSON聚合函数直接生成目标结构:
$q = DB::table('stock_parts') ->select('stock_parts.id', 'stock_parts.title') // 原有总统计 ->selectRaw('COUNT(stock_items.id) AS available') ->selectRaw('SUM(shipments.item_cost) AS totalValue') // 按供应商聚合的临时字段 ->selectRaw('JSON_OBJECT( "supplier_id", shipments.supplier_id, "available", COUNT(stock_items.id), "totalValue", SUM(shipments.item_cost) ) AS seller_stat') ->leftJoin('stock_items', function($join) use ($stockId) { $join->on('stock_items.stock_part_id', '=', 'stock_parts.id') ->where('stock_items.current_stock_id', $stockId); }) ->leftJoin('shipments', 'shipments.id', '=', 'stock_items.shipment_id') // 分组维度新增供应商ID ->groupBy('stock_parts.id', 'stock_parts.title', 'shipments.supplier_id') ->orderBy('stock_parts.title', 'ASC') ->get() // 合并同一零件的多组供应商数据 ->groupBy('id') ->map(function ($rows) { $baseData = $rows[0]; // 合并总统计 $baseData->available = $rows->sum('available'); $baseData->totalValue = $rows->sum('totalValue'); // 组装供应商统计数组 $baseData->available_by_seller = $rows ->pluck('seller_stat') ->map(fn($json) => json_decode($json, true)) ->filter(fn($stat) => !is_null($stat['supplier_id'])) ->values(); // 清理临时字段 unset($baseData->seller_stat); return $baseData; }) ->values() ->toArray();
该方案大部分逻辑在SQL层面执行,性能更好,但需要数据库支持JSON聚合函数,代码可读性稍低
内容的提问来源于stack exchange,提问作者anderlaini
相关产品推荐
相关产品推荐

