group by 和 distinct 不能组合实现多字段分组去重,因语义冲突;正确做法是:distinct 用于全字段联合去重,group by 需配合聚合函数或关闭 only_full_group_by,窗口函数 row_number() 可精确控制每组留哪条。

直接说结论:GROUP BY 和 DISTINCT 不能“组合”使用来实现所谓“多字段分组去重”——它们语义冲突,强行混用会报错或结果不可控。真正要达成“按某字段去重,同时保留其他字段值”的效果,得选对方法:要么用 DISTINCT(严格按所有列联合去重),要么用 GROUP BY(必须配合聚合函数或关闭 only_full_group_by),要么用窗口函数(如 ROW_NUMBER())精确控制留哪条。
为什么 DISTINCT field1, field2 不等于“按 field1 去重 + 带上 field2”
这是最常踩的坑。DISTINCT 作用于它后面**所有字段构成的元组**,不是只管第一个字段。比如表里有:
code | cdate | ctotal A | 2026-01-01 | 100 A | 2026-01-02 | 200 B | 2026-01-01 | 150
SELECT DISTINCT code, cdate, ctotal FROM tt 会返回全部 3 行,因为三列组合都不同;SELECT DISTINCT code FROM tt 才返回 A、B 两行。
所以如果你想要“每个 code 只取一条,并带上对应的 cdate 和 ctotal”,DISTINCT 本身做不到——它不承诺留哪一条,也不支持指定优先级。
GROUP BY code 后 SELECT 多个非分组字段为什么会报错
错误信息典型是:Expression #2 of SELECT list is not in GROUP BY clause...,根源是 MySQL 默认开启 sql_mode=only_full_group_by。
- 该模式要求:出现在
SELECT列表里的字段,要么是GROUP BY中的列,要么是聚合函数(如MAX()、MIN()、ANY_VALUE())包裹的 - 所以
SELECT code, cdate FROM tt GROUP BY code是非法的,因为cdate既没分组也没聚合 - 如果硬要关掉这个模式(不推荐),执行
SET sql_mode=(SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY','')),之后GROUP BY code会返回每组第一条(但 MySQL 不保证是哪条,实际取决于存储引擎和执行计划)
真正可控的多字段“逻辑去重”:用 ROW_NUMBER() 窗口函数
MySQL 8.0+ 支持窗口函数,这是最干净、可预测的方案。核心思路:给每个 code 分组内的记录按某个规则(如时间、ID)排序,只取序号为 1 的那条。
MySQL 9.6.0是面向Linux平台的2026年创新版本,核心架构迎来重大革新。其将外键约束与级联操作从InnoDB引擎层上移至SQL层,确保所有数据变更均被完整记录至Binlog,彻底解决了CDC(变更数据捕获)与主从复制中的数据不一致难题。此外,该版本引入container_aware启动选项以原生适配容器环境,并对审计日志进行了组件化重构,为追求极致数据一致性与云原生体验的开发者提供了全新选择。
例如保留每个 code 对应最新 cdate 的那行:
SELECT code, cdate, ctotal
FROM (
SELECT code, cdate, ctotal,
ROW_NUMBER() OVER (PARTITION BY code ORDER BY cdate DESC) AS rn
FROM tt
) t
WHERE rn = 1;
关键点:
-
PARTITION BY code实现“按 code 分组” -
ORDER BY cdate DESC决定哪条排第一(最新日期优先) -
ROW_NUMBER()生成唯一序号,WHERE rn = 1精确筛选 - 如果想留最早、或按 ID 最大、或按业务权重排序,改
ORDER BY子句即可
COUNT(DISTINCT ...) 和 GROUP BY 配合做统计时的常见误用
有人写 SELECT a.field1, COUNT(DISTINCT b.field2) FROM ... GROUP BY a.field1,以为这能“去重后分组统计”,其实完全正确——但容易混淆的是:这里的 DISTINCT 仅作用于 COUNT 内部,不影响外层 GROUP BY 的行为。
真正容易出错的是把 DISTINCT 错放在 GROUP BY 前面,比如:
-
SELECT DISTINCT field1, COUNT(*) FROM t GROUP BY field1→ 语法合法,但DISTINCT在这里完全冗余,因为GROUP BY已保证field1唯一 -
SELECT field1, DISTINCT field2 FROM t GROUP BY field1→ 直接报错,DISTINCT不能出现在GROUP BY查询的中间位置
统计场景下,记住一个原则:去重逻辑在哪一层,DISTINCT 就得嵌套在哪一层函数里,不能跨层混搭。
实际写 SQL 时,先明确你要的是“去重后的唯一组合”(用 DISTINCT)、“分组聚合结果”(用 GROUP BY + 聚合函数)、还是“每组挑一条代表”(用 ROW_NUMBER())。三者目标不同,强行揉在一起只会让逻辑模糊、结果难复现。










