MySQL 8.0版本将ONLY_FULL_GROUP_BY作为默认启用的SQL模式之一,当执行GROUP BY查询时,如果SELECT子句中出现了既不在GROUP BY子句中,也不是聚合函数处理的列,就会触发报错,提示表达式不在GROUP BY子句中且不是聚合列。这种机制是为了保证SQL查询的结果符合SQL标准,避免出现不确定的查询结果。

ONLY_FULL_GROUP_BY模式的作用
ONLY_FULL_GROUP_BY模式的核心作用是规范GROUP BY查询的合法性。在SQL标准中,GROUP BY查询的结果应该是每个分组对应一条记录,SELECT子句中只能出现分组列和聚合函数计算的结果。如果允许非分组列出现在SELECT中,那么这些列的值在分组内可能是多个,数据库无法确定返回哪一个,就会导致结果的不确定性。MySQL 8.0启用该模式,就是为了避免这种不符合标准的查询产生不可预期的结果。
报错场景示例
假设我们有一张用户订单表order_info,表结构如下:
CREATE TABLE order_info (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT,
order_amount DECIMAL(10,2),
order_date DATE
);
如果我们执行以下查询,就会触发非聚合列报错:
SELECT user_id, order_date, SUM(order_amount) FROM order_info GROUP BY user_id;
报错信息通常为:Expression #2 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'order_info.order_date' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by。这是因为order_date既不在GROUP BY子句中,也没有被聚合函数处理。
调整ONLY_FULL_GROUP_BY模式的方法
临时调整(仅当前会话生效)
如果只是临时需要执行不符合ONLY_FULL_GROUP_BY规范的查询,可以只修改当前会话的SQL模式,不会影响其他连接的使用。执行以下SQL语句即可:
-- 查看当前会话的SQL模式 SELECT @@SESSION.sql_mode; -- 移除ONLY_FULL_GROUP_BY模式 SET SESSION sql_mode = REPLACE(@@SESSION.sql_mode, 'ONLY_FULL_GROUP_BY', '');
这种方式调整后,当前会话的查询就不会受ONLY_FULL_GROUP_BY限制,但是断开连接后设置会失效。
全局调整(所有新会话生效)
如果需要让所有新的数据库连接都关闭ONLY_FULL_GROUP_BY模式,可以修改全局SQL模式。执行以下语句:
-- 查看全局SQL模式 SELECT @@GLOBAL.sql_mode; -- 移除全局的ONLY_FULL_GROUP_BY模式 SET GLOBAL sql_mode = REPLACE(@@GLOBAL.sql_mode, 'ONLY_FULL_GROUP_BY', '');
注意这种方式不会影响已经存在的连接,只对新建立的连接生效,而且重启MySQL服务后设置会恢复默认值。
永久调整(重启服务后生效)
如果需要永久关闭ONLY_FULL_GROUP_BY模式,需要修改MySQL的配置文件。在Linux系统中通常是/etc/my.cnf,Windows系统中是my.ini。在[mysqld]配置段下添加或修改sql_mode配置:
[mysqld] sql_mode = STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
这里去掉了原有的ONLY_FULL_GROUP_BY,然后重启MySQL服务即可永久生效。如果要保留其他模式只移除ONLY_FULL_GROUP_BY,可以先查询当前的sql_mode值,然后复制过来删除对应的部分。
不关闭模式的正确查询方案
其实ONLY_FULL_GROUP_BY模式是更符合SQL标准的,建议尽量不关闭该模式,而是通过调整查询语句来适配。针对上面的报错示例,有两种常见的修改方式:
将非聚合列加入GROUP BY子句
如果order_date确实需要和user_id一起作为分组依据,那么把order_date加入GROUP BY即可:
SELECT user_id, order_date, SUM(order_amount) FROM order_info GROUP BY user_id, order_date;
对非聚合列使用聚合函数
如果只需要每个用户的总订单金额,order_date不需要展示,或者只需要取分组内的某个值,比如最大订单日期,那么可以用聚合函数处理:
SELECT user_id, MAX(order_date) AS latest_order_date, SUM(order_amount) FROM order_info GROUP BY user_id;
两种方案的选择建议
如果是旧项目迁移到MySQL 8.0,大量查询不符合ONLY_FULL_GROUP_BY规范,短时间内无法修改所有查询语句,可以先临时或全局关闭该模式,保障项目正常运行。如果是新开发的项目,建议保留ONLY_FULL_GROUP_BY模式,通过调整查询语句来适配,这样能保证查询结果的确定性,也符合SQL标准,避免后续出现数据不一致的问题。
另外需要注意,修改全局或永久的SQL模式时,要确保其他模式符合业务需求,不要随意删除其他必要的SQL模式,比如STRICT_TRANS_TABLES可以保证数据插入的严格性,避免非法数据入库。
MySQL_8.0ONLY_FULL_GROUP_BYGROUP_BY非聚合列修改时间:2026-07-23 21:09:39