mysql 8.0.22 起才支持 count(distinct col1, col2),因旧版解析器仅允许单列,多列去重需构造唯一元组;该语法忽略含 null 的行,null 需用 coalesce 显式转换;group by 中正确用法是 count(distinct job, level) 配合 group by dept,而非错误地将多列加入 group by。

为什么 COUNT(DISTINCT col1, col2) 在 MySQL 8.0+ 才可用?
早期 MySQL 版本(如 5.7)不支持 COUNT(DISTINCT col1, col2) 这种多列去重语法,会直接报错 ERROR 1241 (21000): Operand should contain 1 column(s)。这是因为旧版解析器只允许 DISTINCT 后跟单个表达式,而多列组合去重本质上需要先构造唯一元组再计数。
MySQL 8.0.22 起才正式支持该语法,PostgreSQL 和 SQL Server 也原生支持,但 SQLite 仍不支持(需用子查询模拟)。
- 确认版本:运行
SELECT VERSION(); - 替代方案(兼容旧版):
SELECT COUNT(*) FROM (SELECT DISTINCT col1, col2 FROM t) AS tmp; - 注意子查询方式在大数据量下可能比原生
COUNT(DISTINCT col1, col2)更慢,因需物化临时结果集
COUNT(DISTINCT) 遇到 NULL 怎么算?
COUNT(DISTINCT) 默认忽略所有含 NULL 的行——哪怕只有其中一列是 NULL,整个组合就被跳过。比如 (1, NULL) 和 (NULL, 'a') 都不会计入结果。
- 验证行为:
SELECT COUNT(DISTINCT a, b) FROM (VALUES (1,2), (1,NULL), (NULL,2)) AS t(a,b);返回1,不是3 - 若需把
NULL当作普通值参与去重,得显式转换:COUNT(DISTINCT COALESCE(a, 'NULL_VAL'), COALESCE(b, 'NULL_VAL')) - 注意字符串替换值不能和真实数据冲突,否则造成误合并
多维度去重时 GROUP BY 和 COUNT(DISTINCT) 的配合陷阱
当既要按某字段分组、又要统计每组内多列组合的去重数时,容易误写成 GROUP BY col1, col2,这其实是在统计“每个 (col1,col2) 组合出现几次”,而非“每组内其他维度的去重数”。
- 正确写法(例如:统计每个部门中「职位+级别」的组合数):
SELECT dept, COUNT(DISTINCT job, level) FROM emp GROUP BY dept; - 错误写法:
SELECT dept, job, level, COUNT(*) FROM emp GROUP BY dept, job, level;—— 这是分组计数,不是去重统计 - 性能提示:如果
job和level基数高,COUNT(DISTINCT job, level)可能触发临时表或磁盘排序,建议给(dept, job, level)建联合索引
用 CONCAT 模拟多列 DISTINCT 容易踩的坑
有人用 COUNT(DISTINCT CONCAT(col1, '|', col2)) 兼容老版本,但这很危险——分隔符可能出现在原始数据里,导致不同组合被拼成相同字符串。
- 反例:
CONCAT('a|b', '|', 'c')和CONCAT('a', '|', 'b|c')都变成'a|b|c',误判为重复 - 更安全的模拟(仅限字符串列):
COUNT(DISTINCT CONCAT_WS('@@', col1, col2)),前提是确认@@不会出现在业务数据中 - 数值列慎用拼接:浮点数精度、整数前导零丢失都可能破坏唯一性,优先走子查询方案











