with()不能直接sum('orders.amount'),因其仅预加载、不JOIN表,sum()只作用于主模型字段;聚合必须用join()+group()或子查询实现。

因为 with() 只做预加载,不 JOIN 表,压根没把关联字段拉进主查询上下文,自然没法 SUM/COUNT。
with() 为什么不能直接 sum('orders.amount')
你写的 UserModel::with('orders')->sum('orders.amount') 会返回 0 或报错,不是语法问题,而是逻辑失效:with() 在底层是「先查用户,再用 IN 查询订单」,两条独立 SQL,sum() 只作用于主模型(User)的字段,根本触达不到 orders 表的 amount 字段。
- with() 返回的是嵌套对象结构(如
$user->orders是集合),不是 JOIN 后的扁平结果集 -
sum()、count()等聚合方法只对当前 Query 对象生效,不穿透到预加载的关联模型 - 即使你调用
$user->orders->sum('amount'),那也只是 PHP 层遍历内存数据求和,不是数据库聚合,性能差且无法加 WHERE 条件下推
想聚合必须显式 JOIN 或子查询
真正能走数据库聚合的只有两种可靠路径:
- 用
join()手动关联 +group()分组:UserModel::field('users.id, users.name, sum(orders.amount) as total')->join('orders', 'users.id = orders.user_id')->group('users.id') - 用子查询封装聚合逻辑:
Db::table('orders')->where('status', 1)->group('user_id')->selectSub('order_sum'),再和 User 关联 - 别指望
withCount()能替代sum()—— 它只支持 COUNT 关联条数,不支持字段值计算
withJoin() 也不是万能解药
withJoin('orders') 看似自动 JOIN,但它只适用于一对一关联(如 hasOne / belongsTo)。对一对多(如用户有多个订单),它会把一个用户展开成多行,导致 sum() 错误放大,分页总数也不准。
立即学习“PHP免费学习笔记(深入)”;
- 一对多场景下
withJoin()生成 LEFT JOIN,用户数据重复 N 次,sum()会重复累加 -
withJoin()不支持嵌套(如withJoin('orders.address')直接报错)、不支持闭包条件、不自动过滤软删除 - 如果真要 JOIN 多层或带复杂条件,手写
join()+ 子查询更可控,也更容易调试(用getLastSql()看生成语句)
最易被忽略的一点:聚合时 GROUP BY 必须和 SELECT 中的非聚合字段严格一致,否则 MySQL strict 模式下直接报错,而 ThinkPHP 默认不提示具体原因——建议开发期始终开启 'show_sql' => true,盯着实际执行的 SQL 看。



















