MySQL严格模式下SELECT混入未分组字段会报错,非分组字段须用聚合函数或加入GROUP BY;空格、NULL影响分组精度;JOIN后需预聚合防重复;取最新记录应优先用窗口函数而非GROUP BY。
SELECT里混入未分组字段,MySQL严格模式直接报错
MySQL 5.7+ 默认开启严格模式,
会触发
。这不是bug,是规则强制:所有非分组字段必须被聚合函数包裹,或出现在
列表中。
常见翻车现场:
写成
——
和
既没聚合也没分组,MySQL 拒绝执行
依赖旧版 MySQL 5.6 的“宽松模式”开发,上线后换环境立刻崩
解决方式只有两种:要么把
包进
、
,要么把它们加进
——但后者可能让本该合并的行被拆开。
看似“重复”的分组键,其实是NULL或空格惹的祸
确实会合并相同值,但“相同”是数据库层面的判定,不是肉眼判断。比如:
(带尾部空格)和
在多数数据库里是不同值;
虽然被归为一组,但业务上常要求它不参与去重。
排查建议:
用
看是否因空格导致分组过细
对敏感字段统一处理:
若想让
不参与分组逻辑,改用
占位
别跳过这步——很多“重复分组”问题查到最后,都是
没加。
JOIN之后再GROUP BY,重复是必然结果
一对多关联(如用户表 × 订单明细表)不做预聚合,直接
,会导致一个用户对应N条明细,
变成N倍,
也重复累加。
正确做法不是在外层硬套
,而是提前收敛:
子查询先聚合:
,再跟用户表
或用
+ 子查询聚合替代直接多对多
检查
输出,确认子查询是否命中
索引;没走索引时,
可能因临时表限制截断结果
外层
连一堆字段进来,再加
,基本等于在给自己埋雷。
想取每组最新一条完整记录?别碰GROUP BY
用
配合
取最新订单,大概率漏数据:同一秒下多单就只取到其中一条;更糟的是,如果
不是唯一约束,还可能匹配到其他用户的同时间记录。
真正可靠的做法:
支持窗口函数的数据库(MySQL 8.0+、PostgreSQL、Oracle):用
,外层筛
不支持窗口函数的老版本:相关子查询
,但得接受并列时间带来的不确定性
绝对不要写
——
和其他字段根本不在同一行
GROUP BY 的设计目标从来不是“挑一行”,而是“归一类”。拿它当去重工具,迟早踩坑。
SELECT a, b, COUNT(*) FROM t GROUP BY aSQLSTATE[42000]: Syntax error or access violationGROUP BYSELECT user_id, order_no, created_at FROM orders GROUP BY user_idorder_nocreated_atorder_noMAX()MIN()GROUP BYGROUP BY'abc ''abc'NULLSELECT LENGTH(TRIM(name)), name FROM t GROUP BY TRIM(name)GROUP BY LOWER(TRIM(email))NULLCOALESCE(email, '') TRIM()GROUP BY user_idCOUNT(*)SUM(amount)DISTINCT(SELECT user_id, SUM(amount) total FROM order_items GROUP BY user_id)JOINLEFT JOINJOINEXPLAINuser_idGROUP BYSELECT *GROUP BYMAX(created_at)JOINcreated_atROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC)rn = 1WHERE created_at = (SELECT MAX(created_at) FROM orders o2 WHERE o2.user_id = o1.user_id)SELECT *, MAX(created_at) FROM orders GROUP BY user_idMAX(created_at)