GROUP BY与HAVING联用SQL报错:非聚合列未在GROUP BY子句中
嘿,我帮你分析下这个报错的原因,再给几个靠谱的解决办法!
为什么会报错?
你遇到的这个错误,核心是MySQL的only_full_group_by模式在起作用——这是MySQL默认开启的严格模式,要求SELECT列表里的所有非聚合列必须出现在GROUP BY子句里,或者这些列在功能上依赖于GROUP BY的列(比如GROUP BY的是主键,那其他列自然依赖它)。
你的子查询SELECT id FROM users GROUP BY firstname HAVING count(*) > 1里,按firstname分组,但要查询的id是非聚合列,而且同一个firstname对应多个不同的id(不然你也不会查重复项了),这就违反了only_full_group_by的规则,所以直接报错。
靠谱的解决方法
方法1:调整子查询的逻辑(最直接)
你的需求是找出所有拥有重复firstname的用户,那完全可以让子查询只返回重复的firstname值,再用这个去筛选原表:
SELECT * FROM users WHERE firstname IN ( SELECT firstname FROM users GROUP BY firstname HAVING COUNT(*) > 1 )
这个写法完全符合only_full_group_by的要求,子查询里SELECT和GROUP BY的都是firstname,逻辑也和你的需求匹配——找出所有属于重复名字组的用户。
方法2:用窗口函数(更灵活)
如果你需要更复杂的筛选(比如只取每个重复组里的前N条记录),窗口函数是更好的选择,它不需要GROUP BY,也不会触发严格模式的限制:
SELECT * FROM ( SELECT *, -- 按firstname分组,计算每个组的总记录数 COUNT(*) OVER(PARTITION BY firstname) AS name_duplicate_count FROM users ) AS user_with_duplicate_count WHERE name_duplicate_count > 1
这个查询会给每条记录加上它所属firstname组的总数量,然后筛选出数量大于1的记录,逻辑清晰还能扩展。
方法3:临时关闭only_full_group_by(不推荐)
如果只是临时测试,不想改SQL,可以临时关闭这个严格模式,但不建议长期这么做(会导致SQL结果不符合预期,违反ANSI标准):
-- 临时关闭当前会话的only_full_group_by SET sql_mode = REPLACE(sql_mode, 'ONLY_FULL_GROUP_BY', '');
执行完这条命令后,你的原SQL就能运行了,但重启MySQL后会恢复原来的设置。
总结
优先推荐方法1或方法2,这两种写法既符合SQL规范,又能准确实现你的需求,避免后续出现其他问题。
内容的提问来源于stack exchange,提问作者Md Hasibur Rahaman

