聚合函数是否需GROUP BY?无GROUP BY时SQL报错原因解析
先还原你的问题场景和遇到的问题:
问题背景
你有两张表:
CITY:包含城市人口Population和国家代码CountryCode字段COUNTRY:包含大洲名称Continent和国家代码Code字段
需求是查询所有有城市的大洲,以及对应城市的人口平均值(向下取整),通过CITY.CountryCode和COUNTRY.Code关联两张表。
你最初写的SQL语句无法运行:
SELECT COUNTRY.CONTINENT, FLOOR(AVG(CITY.POPULATION)) FROM COUNTRY INNER JOIN CITY ON COUNTRY.CODE=CITY.COUNTRYCODE
得到的报错信息是:
ERROR 1140 (42000) at line 1: In aggregated query without GROUP BY, expression #1 of SELECT list contains nonaggregated column 'run_y53padyvlle.COUNTRY.continent'; this is incompatible with sql_mode=only_full_group_by
而添加GROUP BY COUNTRY.CONTINENT后,语句就能正常执行:
SELECT COUNTRY.CONTINENT, FLOOR(AVG(CITY.POPULATION)) FROM COUNTRY INNER JOIN CITY ON COUNTRY.CODE=CITY.COUNTRYCODE GROUP BY COUNTRY.CONTINENT
接下来解答你的两个疑问:
1. 为什么不加GROUP BY会报错?
这个报错的核心原因是你的MySQL开启了only_full_group_by模式——这是MySQL 5.7及以后版本的默认SQL模式,它严格遵循SQL标准的规则。
这个模式的核心要求是:在使用聚合函数(比如AVG、SUM、COUNT)的查询中,SELECT列表里的所有非聚合列(也就是没有被聚合函数包裹的列),必须出现在GROUP BY子句中。
回到你的初始语句:你SELECT了COUNTRY.CONTINENT(这是一个非聚合列,每个大洲对应多个城市),同时又用AVG(CITY.POPULATION)计算了人口平均值。如果没有GROUP BY,数据库会计算所有城市的全局平均人口,但此时SELECT里的Continent有多个不同的值,数据库根本不知道该把哪个大洲和这个全局平均值绑定在一起——是返回亚洲?欧洲?还是随机返回一个?所以为了避免这种歧义,only_full_group_by模式直接抛出错误,而不是返回一个不可靠的结果。
如果关闭这个模式,MySQL确实会允许这种写法,但会随机返回一个大洲的值搭配全局平均值,这显然不符合你的需求,而且这种写法也不符合SQL标准,不推荐使用。
2. 聚合函数是否必须搭配GROUP BY使用?
答案是不一定,分两种情况来看:
- 情况一:全局聚合,不需要关联非聚合列
如果你的查询只是计算整个数据集的单一聚合结果,不需要同时返回其他非聚合列,那么完全不需要GROUP BY。比如:
这个语句没有任何问题,因为它只返回一个聚合结果,不需要分组。-- 计算所有城市的平均人口 SELECT FLOOR(AVG(CITY.POPULATION)) FROM CITY; - 情况二:聚合结果需要和非聚合列一起返回
只要你的SELECT列表里同时包含聚合函数和非聚合列,就必须用GROUP BY把非聚合列作为分组依据。否则在only_full_group_by模式下会报错,即使关闭该模式,结果也不可预测(因为数据库会随机选择非聚合列的某一行值)。
总结一下:当你需要按某个维度(比如大洲、国家)分组计算聚合值时,必须搭配GROUP BY;如果只是计算整个数据集的单一聚合值,就不需要GROUP BY。
内容的提问来源于stack exchange,提问作者Praveen Kumar

