两种去重的关系
SELECT DISTINCT a, b 与 SELECT a, b … GROUP BY a, b 在结果层面等价:都按这些列的组合去重,多个 NULL 也被当成同一组。区别在扩展能力:GROUP BY 能在同一层加聚合列(COUNT、SUM 等),DISTINCT 只能去重,不能顺带出统计值。
基本信息
| 结果行 | 两者都按选定列的组合去重 |
|---|---|
| 扩展能力 | GROUP BY 可加聚合列,DISTINCT 不能 |
| NULL 处理 | 多个 NULL 视为同一组 |
| 严格模式 | 改写后注意 ONLY_FULL_GROUP_BY 对非聚合列的限制 |
| 执行方式 | 都可能有排序或临时表,也可借索引顺序优化 |
典型使用场景
- 列出出现过的城市与状态组合
- 需要「去重后再计数」时必须换成 GROUP BY
- 按组合分组后加统计值再筛 HAVING
- 排查 DISTINCT 结果行数比预期多或少的原因
- 把报表口径从去重列表改成每组一行
示例
示例
-- 两种写法结果相同
SELECT DISTINCT city, status FROM users;
-- 等价写法
SELECT city, status FROM users GROUP BY city, status;
-- 需要附加聚合时必须用 GROUP BY
SELECT city, status, COUNT(*) AS cnt
FROM users
GROUP BY city, status
HAVING COUNT(*) > 10; -- 只保留组合数足够多的分组两种写法对照
| 维度 | SELECT DISTINCT | GROUP BY |
|---|---|---|
| 结果行 | 去重后的组合 | 每组一行 |
| 聚合列 | 不能添加 | 可以添加 |
| 筛选用法 | 只能靠 WHERE | WHERE 过滤行、HAVING 过滤组 |
| NULL | 多个 NULL 视为同一组 | 同样合并为一组 |
| 排序 | 通常要额外排序或临时表 | 同样,可借索引顺序省掉 |
DISTINCT 的去重范围是 SELECT 列表里的全部列:少写一列,结果行数就可能变多,这是「以为去重了其实没有」最常见的来源。
MySQL 的 ONLY_FULL_GROUP_BY 开启时,选择列表里的非聚合列必须出现在 GROUP BY 中;从 DISTINCT 改写过来时先把列补齐,否则直接报错或语义发生变化。
按某一列去重同时保留其他列的值,两者都做不到:DISTINCT 看的是整行组合,GROUP BY 看的是分组键。要保留某条代表记录就必须显式定义口径,例如取时间最新的一条。
两者都可以借助索引顺序省掉排序:去重或分组的列如果正好是某个索引的最左前缀,执行计划里就不会出现额外的排序或临时表。