You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何优化Laravel关联查询以减少模型实例数量?

Laravel三层关联查询优化方案

问题根源

你当前仅预加载了method1(Test1到Test2的关联),但Test2到Test3的method2关联未通过嵌套预加载处理。即使查询次数不多,每个Test2模型都会单独加载关联的Test3数据,导致模型实例数量随数据量呈倍数级增长。以下是具体优化方案:


1. 嵌套预加载关联

直接预加载嵌套的method2关联,让Laravel一次性拉取所有层级的数据,避免重复生成模型实例:

// 控制器修改后的查询代码
$test1s = Test1::with('method1.method2')->get();

此操作只会触发3次查询(分别查询test1s、test2s、test3s),所有关联数据一次性加载完成。

2. 只加载必要字段

避免拉取数据表中所有字段,减少模型实例的内存占用。注意必须包含关联外键,否则无法建立关联关系:

$test1s = Test1::select('id', 'name') // 仅选择Test1需要展示的字段
    ->with([
        'method1' => function ($query) {
            $query->select('id', 'test1_id', 'title'); // 必须包含test1_id(关联外键)
        },
        'method1.method2' => function ($query) {
            $query->select('id', 'test2_id', 'content'); // 必须包含test2_id(关联外键)
        }
    ])->get();

3. 限制关联数据数量

如果不需要展示所有Test3数据,可在预加载时添加约束,只加载符合条件的子集:

$test1s = Test1::with([
    'method1' => function ($query) {
        $query->select('id', 'test1_id', 'title')
            ->with([
                'method2' => function ($subQuery) {
                    $subQuery->select('id', 'test2_id', 'content')
                        ->latest() // 按创建时间倒序
                        ->take(5); // 仅加载前5条Test3数据
                }
            ]);
    }
])->get();

4. 分页加载数据

当数据量较大时,用分页替代全量查询,大幅减少单次加载的模型实例总数:

$test1s = Test1::select('id', 'name')
    ->with([
        'method1' => function ($query) {
            $query->select('id', 'test1_id', 'title');
        },
        'method1.method2' => function ($query) {
            $query->select('id', 'test2_id', 'content');
        }
    ])->paginate(20); // 每页加载20条Test1数据

5. 直接查询返回数组(跳过模型)

如果不需要使用Laravel模型的方法,仅需展示数据,可直接用DB门面执行关联查询,返回数组集合,彻底避免生成大量模型实例:

$data = DB::table('test1s')
    ->select(
        'test1s.id as test1_id',
        'test1s.name as test1_name',
        'test2s.id as test2_id',
        'test2s.title as test2_title',
        'test3s.id as test3_id',
        'test3s.content as test3_content'
    )
    ->leftJoin('test2s', 'test2s.test1_id', '=', 'test1s.id')
    ->leftJoin('test3s', 'test3s.test2_id', '=', 'test2s.id')
    ->get()
    ->groupBy('test1_id'); // 按Test1分组,模拟关联结构

6. 检查模型默认预加载

如果Test2模型中设置了protected $with = ['method2'];,会全局默认加载所有Test2的关联Test3数据。若不需要全局加载,可移除该配置;若需保留,可在查询时临时取消:

$test1s = Test1::with(['method1' => function ($query) {
    $query->without('method2'); // 临时取消Test2的默认预加载
}])->get();

内容的提问来源于stack exchange,提问作者Studlara

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 11:44:51