
本文详解 Laravel Eloquent 中 distinct() 方法在结合 get() 与聚合函数(如 count())时的行为差异,明确其仅对 SELECT 字段生效、不支持参数化字段去重,并提供正确写法与实战注意事项。
本文详解 laravel eloquent 中 `distinct()` 方法在结合 `get()` 与聚合函数(如 `count()`)时的行为差异,明确其仅对 select 字段生效、不支持参数化字段去重,并提供正确写法与实战注意事项。
在 Laravel 中,distinct() 是一个常被误解的查询构建器方法。它并非按指定字段去重(如 distinct('product_id')),而是作用于整个 SELECT 列表——即对所有选中的列组合进行去重。因此,像 ->distinct('product_id') 这样的写法在 Laravel 10+ 中已被废弃,在早期版本中也无效,Eloquent 会忽略该参数并执行全行去重。
你遇到的问题正源于此:
OrderItem::whereIn('order_id', $customers_id)->distinct('product_id')->get();这段代码实际等价于:
OrderItem::whereIn('order_id', $customers_id)->distinct()->get();即对 OrderItem 表中所有被 SELECT *(默认)返回的字段组合去重——只要任意字段不同,整行就被视为唯一,因此结果未按 product_id 归并,看起来“没去重”。
✅ 正确做法分两种场景:
① 统计去重数量(推荐)
若只需统计不同 product_id 的个数,应使用 count() 并显式指定字段(注意拼写修正):
$customers = OrderItem::whereIn('order_id', $customers_id)
->distinct()
->count('product_id'); // ✅ 正确:count() 支持字段参数,distinct() 保证该字段唯一性⚠️ 注意:原答案中 'producr_id' 为拼写错误,应为 'product_id'。
② 获取去重后的完整记录(需明确业务语义)
若需获取每个 product_id 对应的一条 OrderItem 记录(例如最新/最早一条),不能依赖 distinct(),而应使用 groupBy 或窗口函数:
// 方案一:按 product_id 分组,取每组 id 最小的记录
$items = OrderItem::whereIn('order_id', $customers_id)
->selectRaw('MIN(id) as id, product_id, ...') // 显式选择所需字段
->groupBy('product_id')
->get();
// 方案二:Laravel 9+ 支持 subquery + ROW_NUMBER()(更灵活)
$items = OrderItem::whereIn('order_id', $customers_id)
->select('*')
->from(DB::raw("(SELECT *, ROW_NUMBER() OVER (PARTITION BY product_id ORDER BY created_at DESC) AS rn FROM order_items) AS ordered"))
->where('rn', 1)
->get();? 关键总结:
- distinct() 无参数调用才有效,且作用于整个 SELECT 结果集;
- distinct('field') 是错误用法,Laravel 不支持字段级 distinct;
- count('field') 中的字段参数表示“对该字段非 NULL 值计数”,配合 distinct() 才能实现“去重后计数”;
- 真实业务中需要“按某字段去重取记录”时,优先考虑 groupBy 或数据库窗口函数,而非 distinct()。
遵循以上原则,即可避免 distinct() “失效”的困惑,写出语义清晰、结果可靠的查询逻辑。


















