如何通过SQL查询实现品牌与车型的一对多关联数据查询?
问题解答
分步思路的合理性
你的分步思路并非最优方案,虽然能实现需求,但会触发N+1查询问题(1次品牌查询 + N次车型查询),数据量越大性能损耗越明显,不推荐使用。
单条SQL实现方案
完全可以通过单条SQL完成需求,利用LEFT JOIN关联两张表,再借助JSON聚合函数把车型数据聚合到对应品牌下:
原生SQL示例
SELECT b.id AS brand_id, b.name AS brand_name, b.img AS brand_img, JSON_ARRAYAGG( JSON_OBJECT( 'id', m.id, 'name', m.name, 'img', m.img ) ) AS models FROM brands b LEFT JOIN models m ON b.id = m.brand_1 -- 匹配models表的brand_1外键 GROUP BY b.id, b.name, b.img;
这条SQL会将每个品牌的关联车型以JSON数组形式聚合在models字段中,一次查询即可获取所有品牌及其对应车型数据。
Laravel框架中的高效实现
Laravel无需手动编写复杂SQL,通过模型关联就能优雅解决问题,同时避免N+1查询:
- 定义模型关联
在Brand模型中声明与Model的一对多关联:
// app/Models/Brand.php namespace App\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\HasMany; class Brand extends Model { protected $table = 'brands'; protected $fillable = ['id', 'name', 'img']; public function models(): HasMany { // 关联models表的brand_1外键与brands表的id主键 return $this->hasMany(Model::class, 'brand_1', 'id'); } }
- 预加载关联数据
使用with()方法预加载车型,底层会自动执行两条高效SQL(一次查品牌、一次批量查所有关联车型),性能远优于手动分步查询:
$brands = Brand::with('models')->get(['id', 'name', 'img']);
如果坚持要用单条SQL实现,也可以通过查询构造器结合原生聚合函数:
$brands = DB::table('brands as b') ->leftJoin('models as m', 'b.id', '=', 'm.brand_1') ->select([ 'b.id as brand_id', 'b.name as brand_name', 'b.img as brand_img', DB::raw('JSON_ARRAYAGG(JSON_OBJECT("id", m.id, "name", m.name, "img", m.img)) as models') ]) ->groupBy('b.id', 'b.name', 'b.img') ->get();
总结
- 不需要手动分两步遍历查询,该方式性能劣势明显
- 单条SQL可通过
LEFT JOIN + JSON聚合实现需求 - Laravel中优先推荐模型关联+预加载的方式,兼顾简洁性与性能
内容的提问来源于stack exchange,提问作者Max Pattern
相关产品推荐
相关产品推荐

