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

Laravel Eloquent多嵌套表关联:自定义字段与性能优化问题

问题详情

现有模型A、B、C、D及A的类型表A_TYPE,D表含another_system_id字段。需获取关联指定another_system_id的A实体,同时选取A、A_TYPE、D表的指定字段(如foreign_system_id、another_entity_id等)。各模型关联定义如下:

模型关联代码

Model A

public function bs()
{
    return $this->hasMany(B::class);
}
public function b_type()
{
    return $this->belongsTo(BType::class);
}

Model B

public function cs()
{
    return $this->hasMany(C::class);
}

Model C与D的自定义关联

public function system_source()
{
    return $this->hasOne(D::class, 'id', 'datasource_object_id');
}

使用A::with('B.C.system_source')->whereHas(...)查询时,生成的SQL采用多层exists子查询,性能比手写JOIN慢一倍,且get()->count()返回0,但替换参数后手动执行对应SQL有结果。手写的正确SQL如下:

SELECT d.form_id, d.question_id, a.id, a.`name`, a.parent_id, at.`name` as 'A Type'
FROM a
JOIN a_types at ON a.a_type_id = at.id
JOIN b on b.a_id = a.id
JOIN c on c.b_id = b.id
JOIN d on d.id = c.datasource_object_id
WHERE d.another_system_id in (9576, 9575);

当前需解决两个问题:

  1. Eloquent中如何自定义选择所需字段;
  2. 如何让Eloquent生成类似手写的高效JOIN查询,替代低效的exists查询。

解决方案

1. 自定义选择所需字段

Eloquent中通过select()方法指定要查询的字段,关联表的字段需用表别名或完整表名区分,避免字段冲突。结合JOIN查询的示例写法:

$results = A::select(
        'a.id',
        'a.name',
        'a.parent_id',
        'at.name as A_Type',
        'd.form_id',
        'd.question_id'
    )
    ->join('a_types as at', 'a.a_type_id', '=', 'at.id')
    ->join('b', 'b.a_id', '=', 'a.id')
    ->join('c', 'c.b_id', '=', 'b.id')
    ->join('d', 'd.id', '=', 'c.datasource_object_id')
    ->whereIn('d.another_system_id', [9576, 9575])
    ->get();

若需保留Eloquent模型特性(如后续调用模型方法),可选择主表所有字段,再附加关联表所需字段:

$results = A::select('a.*', 'at.name as A_Type', 'd.form_id', 'd.question_id')
    ->join('a_types as at', 'a.a_type_id', '=', 'at.id')
    ->join('b', 'b.a_id', '=', 'a.id')
    ->join('c', 'c.b_id', '=', 'b.id')
    ->join('d', 'd.id', '=', 'c.datasource_object_id')
    ->whereIn('d.another_system_id', [9576, 9575])
    ->get();

2. 生成高效JOIN查询替代exists

whereHas()默认生成多层exists子查询,关联层级多、数据量大时性能损耗明显。直接使用join()方法可生成与手写SQL一致的JOIN语句,规避多层子查询的性能问题。

注意事项:

  • 确保JOIN关联条件与模型定义的关联逻辑完全匹配(如C和D的关联为d.id = c.datasource_object_id);
  • 一对多关联(如A hasMany B)JOIN后可能出现重复A记录,需用distinct()去重:
$results = A::select('a.*', 'at.name as A_Type', 'd.form_id', 'd.question_id')
    ->join('a_types as at', 'a.a_type_id', '=', 'at.id')
    ->join('b', 'b.a_id', '=', 'a.id')
    ->join('c', 'c.b_id', '=', 'b.id')
    ->join('d', 'd.id', '=', 'c.datasource_object_id')
    ->whereIn('d.another_system_id', [9576, 9575])
    ->distinct()
    ->get();
  • 统计符合条件的A实体数量时,使用count('distinct a.id')避免重复计数:
$count = A::join('a_types as at', 'a.a_type_id', '=', 'at.id')
    ->join('b', 'b.a_id', '=', 'a.id')
    ->join('c', 'c.b_id', '=', 'b.id')
    ->join('d', 'd.id', '=', 'c.datasource_object_id')
    ->whereIn('d.another_system_id', [9576, 9575])
    ->count('distinct a.id');

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 23:43:26