<p>GROUP BY 不能直接去重,仅分组且需配合聚合函数或子查询实现清洗;严格模式下SELECT * GROUP BY会报错,非严格模式下结果随机;删除重复行须先定位再用JOIN或窗口函数。</p>

GROUP BY 本身不清洗数据,只是分组结果
直接写 SELECT * FROM orders GROUP BY order_id 看起来像去重,但这是危险的错觉。MySQL 5.7+ 默认开启 sql_mode=ONLY_FULL_GROUP_BY,这种写法会报错:Expression #2 of SELECT list is not in GROUP BY clause;即使关掉严格模式,它也只是随机取每组第一行,无法保证取到“最新”或“有效”的那条。真正的清洗必须明确:你要保留哪一条?依据是什么?
用 GROUP BY + 聚合函数“模拟”去重时,必须选对聚合逻辑
比如订单表里 order_id 重复,但 order_time 和 amount 完全一致,说明是双写脏数据。这时你可以用 MIN() 或 MAX() 取时间、金额等字段,但要注意:
-
MIN(order_time)和MAX(order_time)在完全重复行里结果一样,安全 - 如果重复行的
amount不同(比如一次写入 59.90,另一次写入 59.9),MAX(amount)就可能掩盖错误,得先查清业务含义 - 想取整行记录(不只是几个字段),不能只靠
GROUP BY,必须配合子查询或JOIN
典型写法:
SELECT order_id, user_id, MAX(order_time) AS order_time, MAX(amount) AS amount FROM orders GROUP BY order_id, user_id;
真正要删重复行,GROUP BY 只能辅助定位,不能直接删除
SQL 标准里 DELETE 不支持直接 GROUP BY。你得先用 GROUP BY 找出重复键,再通过子查询或 CTE 删除多余行。例如删掉除 MIN(id) 外的所有重复 order_id:
- 先确认哪些
order_id重复:SELECT order_id, COUNT(*) FROM orders GROUP BY order_id HAVING COUNT(*) > 1 - 再删掉非首行:
DELETE o1 FROM orders o1 INNER JOIN orders o2 WHERE o1.order_id = o2.order_id AND o1.id > o2.id;
- 注意:这个写法依赖自增
id字段;如果没有主键或唯一标识,就得用窗口函数先标序号
GROUP BY 在去重场景下最容易被忽略的三个坑
NULL 值会被归为同一组,但业务上往往不能等同处理;GROUP BY 对字符串大小写敏感性取决于 collation,'O002' 和 'o002' 可能被当成两组;还有就是性能——没索引的 GROUP BY order_id 在千万级表上可能跑十几分钟。别等执行完才发现卡住,先看 EXPLAIN 输出里有没有 Using temporary; Using filesort。

















