SQL去重应据需求选DISTINCT或GROUP BY:DISTINCT适合单纯取唯一值,GROUP BY适合聚合或按多字段去重;务必为去重字段建索引,用EXPLAIN验证执行计划;复杂场景优先用窗口函数替代低效子查询。

SQL去重查询核心靠 DISTINCT 和 GROUP BY,但选对方法、加对索引、避开陷阱,才能真正高效。不是所有“去重”都该用 DISTINCT,也不是 GROUP BY 一定比它慢。
用 DISTINCT 还是 GROUP BY?看需求再决定
DISTINCT 适合单纯取唯一值,语义清晰、写法简单;GROUP BY 更适合需要聚合计算(如 COUNT、SUM)或按多字段组合去重的场景。
- 只要“姓名不重复”,用
SELECT DISTINCT name FROM users; - 要“每个城市最新一条用户记录”,就不能只靠 DISTINCT,得结合子查询或窗口函数
- 如果去重字段没索引,DISTINCT 在大数据量下会触发 filesort 或临时表,性能明显下降
避免全表扫描:给去重字段加索引
数据库对 DISTINCT 或 GROUP BY 的优化,高度依赖索引。尤其当去重字段出现在 WHERE 条件之后时,复合索引能大幅提速。
- 例如常执行
SELECT DISTINCT status, category FROM orders WHERE create_time > '2024-01-01';,建议建联合索引:(create_time, status, category) - 注意:NULL 值在多数索引中会被单独处理,含大量 NULL 的字段单独建索引效果有限
- 用
EXPLAIN检查执行计划,确认是否用了索引(key 列非 NULL,type 不是 ALL)
大数据量别硬扛:用 ROW_NUMBER() 替代低效子查询
当需要“每组取一条最新/最旧记录”时,容易写出带相关子查询的写法,性能极差。窗口函数更可控、更易读。
- ❌ 低效写法:
SELECT * FROM users u1 WHERE id = (SELECT MAX(id) FROM users u2 WHERE u2.city = u1.city); - ✅ 推荐写法:
SELECT * FROM (SELECT *, ROW_NUMBER() OVER (PARTITION BY city ORDER BY id DESC) rn FROM users) t WHERE rn = 1; - 注意:MySQL 8.0+、PostgreSQL、SQL Server 都支持;老版本 MySQL 可用变量模拟,但需谨慎测试顺序
临时去重?考虑用临时表或物化中间结果
复杂报表中多次用到同一份去重数据,反复计算浪费资源。可先存入临时表,再复用。
CREATE TEMPORARY TABLE tmp_distinct_users AS SELECT DISTINCT user_id, region FROM log_table WHERE dt = '2024-05-01';- 临时表自动在会话结束时销毁,不占长期空间;加上索引后,后续 JOIN 效率更高
- 若需跨会话共享,可用普通表 + 明确清理逻辑,避免堆积
基本上就这些。去重不是炫技,关键是匹配场景、减少数据搬运、让数据库少做无用功。索引、执行计划、数据分布,三者看准了,效率自然上来。










