Laravel Eloquent 按商品分组查询:取订单数最高商品的最低价格记录
问题原因
- 原代码写
groupBy('product_id')的时候,没有给分组内的字段设定取数规则,MySQL默认会捞每个分组里物理存储最靠前的那条记录返回,所以你拿到的是product_id=1下id=1、售价2.99的记录,不是你要的售价1.99的那条。 - 当前的排序顺序不对:直接把订单数排序放在分组前面,没有约束同商品下取价格最低记录的规则,和实际需求不匹配。
调整写法
整个查询的执行逻辑要顺着顺序走:先给同一个product_id下的所有记录按价格从低到高排 → 再按product_id做分组截取(这时候数据库每个分组捞的第一条,就是该商品下价格最低的那条) → 最后对每个商品分组的订单总数做倒序排 → 取排名第一的分组就行。
兼容常规MySQL版本的代码如下:
Items::query() ->withCount(['orders' => function($q){ // 保留你原来的订单状态过滤逻辑 $q->active(); }]) // 关键:分组前先按价格升序,让同product_id下价格最低的记录排在组内最前面 ->orderBy('price', 'asc') ->groupBy('product_id') // 分组完成后,再按关联订单数倒序,找到订单数最高的商品 ->orderBy('orders_count', 'desc') ->limit(1) ->get();
踩坑提醒
- 如果你的MySQL开了
ONLY_FULL_GROUP_BY严格模式,直接跑上面的写法会报未聚合字段的错误,这时候要把withCount的关联统计换成等价的子查询写法,保证SELECT的所有字段都符合分组规则:
Items::query() ->select('id', 'product_id', 'price', // 这里的过滤条件要和你原来的active()逻辑完全一致 \DB::raw('(SELECT COUNT(*) FROM orders WHERE orders.product_id = items.product_id AND orders.status = 1) as orders_count') ) ->orderBy('price', 'asc') ->groupBy('product_id') ->orderBy('orders_count', 'desc') ->first();
- 拿你给出的示例数据校验:product_id=1的有效订单一共3条,比product_id=2的2条多;同product_id=1的记录按价格升序排完,1.99的记录在组内排第一,分组的时候就会捞到这条,完全符合你要的结果。
- 别把排序顺序搞反:如果把按订单数排序的代码放到分组前面,会导致每个分组里捞到的第一条不是价格最低的,最后返回的结果还是错的。
内容的提问来源于stack exchange,提问作者Mr.SH
相关产品推荐
相关产品推荐

