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

Laravel Eloquent toSql()未显示关联表问题咨询

Laravel 预加载关联的SQL查询疑问

问题现象

使用Laravel的with("relationship")预加载关联时,能正确返回包含关联数据的结果,但调用toSql()获取SQL时,只得到主表的查询语句,完全看不到关联表的查询:

执行以下代码能正常返回包含exercise_records的结果:

json_encode(User::find(1)->materials()->where("id", 1)->with("exerciseRecords")->get()->toArray());

但执行toSql()时:

User::find(1)->materials()->where("id", 1)->with("exerciseRecords")->toSql();

得到的SQL仅为:

select * from `materials` where `materials`.`user_id` = ? and `materials`.`user_id` is not null and `id` = ?

疑问:这是不是说明框架在循环获取关联数据(N+1查询)?还是toSql()本身有局限?

解答

  1. toSql()的局限性
    toSql()只能返回当前查询构造器实例对应的SQL语句,也就是你当前构建的主查询(这里是materials表的查询)。而with()定义的预加载关联,是Laravel在主查询执行完成后,单独发起的另一条查询,所以不会被包含在toSql()的结果里。

  2. with()是预加载,不是N+1查询
    Laravel的with()预加载机制是“预加载”,不是循环查询:

  • 第一步:执行主查询,获取符合条件的materials记录;
  • 第二步:提取这些materials的主键,用IN条件一次性查询关联的exercise_records表;
  • 最后框架自动把关联数据匹配到对应的主模型上。
    整个过程只会产生2条SQL,不是每条主记录都发起一次关联查询(那才是N+1问题)。
  1. 如何查看所有预加载的SQL
    要看到所有执行的SQL(包括预加载的关联查询),可以用Laravel的查询监听功能:
// 在执行查询前添加监听
DB::listen(function ($query) {
    echo "SQL: " . $query->sql . "\n";
    echo "Bindings: " . implode(', ', $query->bindings) . "\n\n";
});

// 执行你的查询
User::find(1)->materials()->where("id", 1)->with("exerciseRecords")->get();

执行后你会看到两条SQL:一条是materials的主查询,另一条是exercise_records的关联查询(类似select * from exercise_records where material_id in (?))。

  1. 区分预加载和N+1的方法
    如果去掉with("exerciseRecords"),然后循环遍历materials并访问$material->exerciseRecords,这时才会触发N+1查询——每条material都会单独发起一次关联查询,你可以用上面的监听方法对比两者的SQL数量差异。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 18:02:12