Laravel 7高级内连接关联customers与transactions表 如何将关联交易聚合为数组
问题根因
你当前写的内连接查询,是基于行匹配的关联逻辑。由于客户和交易是1对多的映射关系,每匹配1条交易记录就会生成一行完整的客户基础信息,最终返回的结果里同一个客户会重复出现N行(N为该客户的交易数),从逻辑上就不可能直接得到单客户内嵌全量交易数组的结构。
可落地实现方案
方案1:原生查询构造器+MySQL聚合函数实现
如果你习惯继续用DB查询构造器、不想切到Eloquent模型,可以直接利用MySQL 5.7及以上版本自带的JSON聚合函数,在数据库层面就把同客户的所有交易打包成数组,不用等查完数据在PHP端循环拼接:
$customers = DB::table('customers') // 如果需要过滤无交易的客户可以换回innerJoin,否则用leftJoin可保留所有客户 ->leftJoin('transactions', 'customers.id', '=', 'transactions.custId') ->select( 'customers.*', // 自定义聚合格式,把每笔交易打包为JSON对象后聚合为数组 DB::raw('JSON_ARRAYAGG(JSON_OBJECT( "id", transactions.id, "amount", transactions.amount, "trade_no", transactions.trade_no, "trade_time", transactions.trade_time )) as transactions_list') ) // 必须按客户主键分组,否则聚合结果会把所有客户的交易混到一起 ->groupBy('customers.id') ->get() // 把聚合出的JSON字符串转为可用的PHP数组 ->map(fn($item) => tap($item, function($cust) { $cust->transactions_list = json_decode($cust->transactions_list, true); }));
方案2:Eloquent关联预加载(官方推荐最优解)
既然两表已经通过外键建立了关联,直接使用Laravel内置的模型关联能力,不需要手写join逻辑,性能和可维护性都远高于手写聚合查询:
- 先在Customer模型中定义1对多关联关系:
// app/Models/Customer.php public function transactions() { // 第二个参数对应交易表的外键字段custId,第三个参数对应客户表的主键id return $this->hasMany(Transaction::class, 'custId', 'id'); }
- 查询时通过
with方法预加载关联数据,即可自动实现客户维度的聚合绑定:
$customers = Customer::with('transactions')->get();
这个方法返回的每条客户记录下,transactions属性会自动挂载该客户关联的全部交易集合,输出接口时会自动渲染为嵌套的数组结构,完全匹配你期望的返回样式。
注意事项
- 不要在查询中写
select('customers.*', 'transactions.*'),两个表的同名字段(比如id、created_at)会出现值覆盖,导致数据读取错误 - 预加载关联的方式总共只会执行2条SQL:一次查询全量客户,一次查询所有关联客户的交易,在内存中完成映射绑定,不会出现行重复、冗余数据传输的问题,比手写join聚合的性能更优
- 如果需要过滤关联交易的范围,可以在预加载时传入约束条件,例如只查询已完成的交易:
$customers = Customer::with(['transactions' => function ($query) { $query->where('status', 1)->orderBy('trade_time', 'desc'); }])->get();
内容的提问来源于stack exchange,提问作者Priyanka
相关产品推荐
相关产品推荐

