select distinct col1, col2, col3 from table 按三列完整组合去重,仅当所有字段值完全相同时才视为重复;null 被视为相同值,含主键字段则必然无法去重。

SELECT DISTINCT 多字段组合去重的写法
直接写 SELECT DISTINCT col1, col2, col3 FROM table 就能按这三列的完整组合去重,不是分别对每个字段去重。数据库会把整行看作一个元组,只有所有字段值都相同时才视为重复。
常见错误是误以为 DISTINCT 只作用于第一个字段,比如写成 SELECT DISTINCT user_id, email, created_at 却期待只保留每个 user_id 的最新一条——这不会发生,它只是筛掉完全相同的三元组。
- 如果表里有主键或唯一标识字段(如
id),千万别放进SELECT DISTINCT列表,否则必然无去重效果 -
NULL值会被当作相同值合并:多行(a, NULL)只保留一行 - 字段顺序影响结果但不影响语义,
DISTINCT a, b和DISTINCT b, a返回的组合集合相同,只是排序可能不同
COUNT(DISTINCT col1, col2) 统计组合唯一数
主流数据库(MySQL 5.7+、PostgreSQL、SQL Server、Oracle、SQLite)都支持 COUNT(DISTINCT col1, col2) 直接统计多列组合的唯一数量,语法简洁且性能通常优于子查询。
注意这不是所有数据库都原生支持:旧版 MySQL(5.6 及更早)在某些嵌套场景下会报错;Hive SQL 也不支持该写法,需改用子查询。
- 错误写法:
DISTINCT COUNT(col1, col2)—— 语法非法,数据库直接报错syntax error at or near "DISTINCT" - 字段含空格或大小写差异时,
COUNT(DISTINCT CONCAT(TRIM(city), '-', UPPER(region)))比裸写更可控 - 若其中任一字段为
NULL,整个组合会被忽略(即(1, NULL)不参与计数)
GROUP BY 子查询替代方案(兼容性优先)
当目标数据库不支持 COUNT(DISTINCT a, b),或你需要后续加复杂条件(如过滤某组内最大时间戳)时,用子查询 + GROUP BY 最稳妥:
SELECT COUNT(*) AS unique_count FROM (SELECT col1, col2 FROM table GROUP BY col1, col2) AS t;
这个写法在所有 SQL 引擎中都能跑通,且便于扩展:比如想统计“每个部门中不同职级的数量”,可直接在外层加 WHERE 或与另一张表 JOIN。
- 别漏写子查询的别名(如示例中的
AS t),MySQL 8.0+ 和 PostgreSQL 要求必须有 - 如果组合字段很多(如 5 列),
GROUP BY的哈希开销可能比COUNT(DISTINCT)更高,需实测 - 避免在子查询里做函数计算(如
UPPER(name)),否则索引失效,去重变慢
去重结果少于预期?先查数据本身
多数时候不是 SQL 写错了,而是数据隐含干扰项:末尾空格、大小写、不可见字符、类型隐式转换(比如字符串 '123' 和数字 123 在某些引擎里不等价)。
快速排查步骤:
- 抽一组原始数据:
SELECT col1, col2 FROM table WHERE col1 = 'X' LIMIT 10,肉眼确认是否真重复 - 检查空格:
SELECT col1, LENGTH(col1), HEX(col1) FROM table WHERE col1 LIKE '% %' - 统一大小写再试:
COUNT(DISTINCT LOWER(col1), LOWER(col2)) - 确认 NULL 占比:
SUM(CASE WHEN col1 IS NULL OR col2 IS NULL THEN 1 ELSE 0 END)
真正麻烦的是跨库迁移时字段 collation 不一致,比如 MySQL 的 utf8mb4_0900_as_cs 和 utf8mb4_general_ci 对大小写的处理完全不同——这种问题单靠 SQL 很难绕开,得提前对齐。











