MySQL 8.0 默认启用 ONLY_FULL_GROUP_BY 导致 Laravel GROUP BY 查询报错,因未将非聚合字段加入 GROUP BY 或使用聚合函数;推荐用 ANY_VALUE() 显式声明或重构查询逻辑,而非禁用该模式。
MySQL 8.0 默认启用 ONLY_FULL_GROUP_BY 导致 Laravel 查询报错
这是最常见原因:mysql 8.0 开启了 only_full_group_by sql 模式,而 laravel 的 eloquent 或原生查询中用了 group by 却没把所有非聚合字段放进 group by 子句里,mysql 就直接报错——比如 expression #1 of select list is not in group by clause。
不是 Laravel 写错了,是 MySQL 更严格了。Laravel 生成的查询(尤其是用 select() + groupBy() 组合时)常只写 GROUP BY id,但又选了 name、email 等字段,这在 MySQL 5.7 是容忍的,在 8.0 就挂。
- 别急着关 SQL 模式——临时禁用会掩盖真实问题,上线后可能因数据逻辑出错
- 优先检查你写的
groupBy()查询:是否真需要那些非分组字段?能不能改用聚合函数包裹,比如MAX(name) - 如果业务上确定“每组只有一条记录”,可以用
ANY_VALUE(name)显式声明,MySQL 8.0 认这个
Laravel 中用 ANY_VALUE() 绕过 ONLY_FULL_GROUP_BY 限制
MySQL 8.0 提供 ANY_VALUE() 函数,告诉服务器“我知道这个字段在组内值一致,随便取一个就行”。Laravel 不内置支持它,但可以手写原始表达式。
常见场景:查用户订单数,同时想顺带显示用户名(假设一个用户只对应一个名字)。
DB::table('orders')
->select(
'user_id',
DB::raw('COUNT(*) as order_count'),
DB::raw('ANY_VALUE(users.name) as name')
)
->join('users', 'orders.user_id', '=', 'users.id')
->groupBy('user_id');
-
ANY_VALUE()必须配合GROUP BY使用,否则无意义 - 不能对可能有歧义的字段乱用——比如用户昵称允许修改,历史订单里关联的可能是旧昵称,这时用
MAX(created_at)配合子查询更稳妥 - Eloquent 中无法直接链式调用
ANY_VALUE(),得退到DB::raw()或原生查询
临时关闭 ONLY_FULL_GROUP_BY 的风险与操作方式
开发环境快速验证可以关,但必须清楚代价:关掉后,MySQL 可能返回任意一行的非分组字段值,且不报错——等于把隐患藏起来。
MySQL(Linux)
MySQL 9.6.0是面向Linux平台的2026年创新版本,核心架构迎来重大革新。其将外键约束与级联操作从InnoDB引擎层上移至SQL层,确保所有数据变更均被完整记录至Binlog,彻底解决了CDC(变更数据捕获)与主从复制中的数据不一致难题。此外,该版本引入container_aware启动选项以原生适配容器环境,并对审计日志进行了组件化重构,为追求极致数据一致性与云原生体验的开发者提供了全新选择。
下载
- 运行时临时关(仅当前连接有效):
SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode, 'ONLY_FULL_GROUP_BY', '')); - 永久关需改 MySQL 配置文件
my.cnf,在[mysqld]下加:sql_mode = STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION(注意去掉ONLY_FULL_GROUP_BY) - Laravel 启动时自动执行(不推荐):在
AppServiceProvider::boot()里用DB::statement()执行 SET,但会污染所有连接,且无法保证每次连接都生效
升级 Laravel 版本对 GROUP BY 的影响
Laravel 9.2+ 对 MySQL 8.0 做了些适配,比如 distinct() 在某些场景下会自动补全 GROUP BY 字段,但没解决核心逻辑问题——框架不会替你判断“该不该选那个字段”。
- Laravel 10 的
groupBy()行为没变,依然不会自动把select中的字段加进GROUP BY - 如果你用的是 Scout 或第三方包做聚合,要特别留意它们生成的 SQL 是否符合 MySQL 8.0 规则
- 真正省心的做法:把聚合逻辑收口到数据库视图或存储过程里,让 Laravel 只查视图——这样规则和兼容性都在 DB 层控制
分组查询的麻烦从来不在语法,而在你是否清楚每一列值的来源和确定性。MySQL 8.0 只是把过去被忽略的问题摆到了明面上。