withCount()、withSum()等方法生成单条带子查询SQL,避免N+1;字段名按orders_count、orders_sum_amount等规则命名;条件聚合需用闭包;JOIN适用于需排序、过滤或跨表对齐的场景。

直接用 withCount()、withSum() 这类方法,比手写子查询或循环查快得多,也更安全——它们生成的是单条带子查询的 SQL,不是 N+1。
withCount() 和 withSum() 生成的字段名怎么访问
默认字段名有固定格式,不手动指定就容易取错值:
-
withCount('orders')→ 字段是orders_count,不是count_orders或order_count -
withSum('orders', 'amount')→ 字段是orders_sum_amount,注意中间是_sum_,不是_total_ - 重命名用数组语法:
withCount(['orders as paid_count'])→ 取值用$user->paid_count - 多个聚合同时用,字段名互不干扰:
withCount('orders')->withSum('orders', 'amount')会同时挂载orders_count和orders_sum_amount
条件聚合必须用闭包,不能只传字符串
想统计“已支付订单数”或“完成订单总金额”,不能写成 withCount('orders where status = "paid"')——这会报错或被忽略。
- 正确写法是传数组 + 闭包:
withCount(['orders' => function ($q) { $q->where('status', 'paid'); }]) - 闭包里可以链式调用任意 Query Builder 方法:
->whereNotNull('paid_at')->whereDate('paid_at', '>=', now()->subMonth()) - 注意:闭包里的条件只作用于子查询,不影响主查询的
WHERE,所以不会意外过滤掉用户本身
JOIN 方式聚合 vs withXxx() 的适用边界
不是所有聚合都适合用 withCount() 类方法。当你要把聚合结果和主表字段混排、排序或参与 WHERE 过滤时,就得换 JOIN。
-
withCount()适合:查出用户列表,顺带知道每人几个订单——字段只作展示,不参与筛选 - 要用 JOIN 的典型场景:
WHERE orders_count > 5、ORDER BY orders_sum_amount DESC、或者要和另一张表的聚合结果对齐(比如“用户订单总额”和“同日课程平均值”按日期 LEFT JOIN) - JOIN 写法中,子查询必须显式别名,且
GROUP BY要包含所有非聚合字段,否则 MySQL 8.0+ 直接报错 - 别名冲突高发区:多层 JOIN 时,
id、name这类字段必须加前缀,如users.id、orders.status
闭包里写条件、字段名按规则拼、JOIN 时别名和 GROUP BY 不漏——这几个点漏一个,轻则数据不对,重则查询失败。实际项目里最常翻车的是字段名拼错和 JOIN 条件写进 where() 而不是 on()。


















