如何使用Laravel Query Builder获取age列唯一的完整行数据
问题:获取age列值唯一的完整行数据
表结构与需求
原表数据:
| id | age | color |
|---|---|---|
| 1 | 2 | blue |
| 3 | 3 | red |
| 4 | 5 | green |
| 5 | 5 | purple |
需求:获取所有age列值唯一的行,预期返回:
| id | age | color |
|---|---|---|
| 1 | 2 | blue |
| 3 | 3 | red |
| 4 | 5 | green |
尝试过的方法及问题
1. 使用distinct('age')
代码:
$baseQuery = myModel::query() ->select('id', 'age', 'color') ->distinct('age') ->get()
问题:distinct是针对所有选中列的组合去重,并非单独对age列去重。当同时选择id、age、color时,只要这三列的组合不重复就会被保留,因此最终返回了所有行。只有当select仅包含age时,才会实现age列的去重。
2. 使用groupBy('age')
代码:
$baseQuery = myModel::query() ->select('id', 'age', 'color') ->groupBy('age') ->get()
问题:触发MySQL 1055错误:
Syntax error or access violation: 1055 'table.color' isn't in GROUP BY
这是因为MySQL默认开启了ONLY_FULL_GROUP_BY模式,该模式要求GROUP BY子句中必须包含SELECT里所有非聚合函数的列,否则会报错。
解决方案
方法1:临时关闭ONLY_FULL_GROUP_BY模式
在执行查询前修改SQL模式,再执行分组查询:
\DB::statement("SET SQL_MODE=''"); $baseQuery = myModel::query() ->select('id', 'age', 'color') ->groupBy('age') ->get()
注意:关闭ONLY_FULL_GROUP_BY后,MySQL会从每个age分组中随机选取id和color的值,刚好符合预期结果,但如果需要指定选取分组内的某一行(比如最小id对应的行),这种方法的结果不可控。
更严谨的方法:通过子查询指定分组内的目标行
如果需要确保每个age分组返回特定行(比如id最小的行),可以用子查询先获取每个age对应的最小id,再关联原表:
$baseQuery = myModel::query() ->select('t.id', 't.age', 't.color') ->from('my_table as t') ->join( myModel::query()->selectRaw('age, min(id) as min_id')->groupBy('age'), function ($join) { $join->on('t.age', '=', 'age')->on('t.id', '=', 'min_id'); } ) ->get();
这种方法不需要修改SQL模式,结果更可控,适合需要明确返回分组内特定行的场景。
内容的提问来源于stack exchange,提问作者ThatSWE
相关产品推荐
相关产品推荐

