count(distinct col1, col2) 在多数数据库中不支持,应使用 concat(coalesce(col1,''),'|',coalesce(col2,'')) 或子查询 select count(*) from (select distinct col1, col2 from t where cond) as t。

用 COUNT(DISTINCT ...) 统计多字段组合唯一行数
直接写 COUNT(DISTINCT col1, col2) 会报错 —— 大多数 SQL 引擎(包括 MySQL 5.7、PostgreSQL、SQL Server)不支持对多个列直接用 DISTINCT 参数。必须把多列“打包”成一个可比较的单元。
最通用、兼容性最好的做法是拼接字段,例如:COUNT(DISTINCT CONCAT(col1, '|', col2))。但要注意分隔符必须能确保不产生歧义(比如 col1='a|b' 和 col2='c' 拼成 'a|b|c',和 col1='a'、col2='b|c' 结果一样)。
- MySQL 8.0+ 和 PostgreSQL 支持
COUNT(DISTINCT (col1, col2))(注意括号),这是语义最清晰的写法 - SQLite 也支持
COUNT(DISTINCT col1, col2)(无括号),但行为与其他引擎不一致,慎用 - 如果字段含 NULL,
CONCAT会返回 NULL,导致该行被忽略;改用CONCAT(COALESCE(col1, ''), '|', COALESCE(col2, ''))
用子查询去重后再 COUNT
当拼接方案不可靠(比如字段内容本身含任意字符、长度极大、或需要保留原始类型语义),更稳妥的方式是先 GROUP BY col1, col2 去重,再统计行数。
SELECT COUNT(*) FROM ( SELECT DISTINCT col1, col2 FROM table_name ) AS t;
这个写法在所有主流数据库中都有效,且逻辑明确:先生成唯一组合集合,再数有多少行。
- 性能上,大表可能比拼接方案慢,因为需要物化中间结果集
- 如果只关心数量、不关心具体组合值,这个子查询方式反而更安全,尤其涉及 TEXT/BLOB 或 JSON 字段时
- 别漏掉
AS t—— 大多数引擎要求子查询必须有别名,否则报错Every derived table must have its own alias
WHERE 条件下统计组合唯一数
带过滤条件时,不能把 WHERE 写在子查询外层(那样会先全表去重再过滤),必须让条件作用于原始数据。
- 错误写法:
SELECT COUNT(*) FROM ( SELECT DISTINCT col1, col2 FROM table_name ) AS t WHERE col1 > 10;
(col1在外层已不可见) - 正确写法:把
WHERE放进子查询里SELECT COUNT(*) FROM ( SELECT DISTINCT col1, col2 FROM table_name WHERE col1 > 10 AND col2 IS NOT NULL ) AS t;
- 若用拼接方案,
WHERE同样要放在拼接之前,避免对 NULL 或无效数据做 CONCAT
不同数据库的语法差异和坑
看似简单的需求,实际跨库移植时最容易翻车。
- MySQL 5.7 不支持
COUNT(DISTINCT (a,b)),但 MySQL 8.0+ 支持;升级前务必验证 - PostgreSQL 允许
COUNT(DISTINCT (a,b)),但括号不能省 ——COUNT(DISTINCT a,b)是语法错误 - SQL Server 不支持多列
DISTINCT,也不支持CONCAT(得用+拼接,且需手动处理 NULL:ISNULL(col1,'') + '|' + ISNULL(col2,'')) - Oracle 需用
COUNT(DISTINCT col1||'|'||col2),双竖线是字符串连接符
字段类型混用(比如整数和字符串拼接)时,隐式转换可能出错;显式转成字符串更可控,比如 MySQL 用 CAST(col1 AS CHAR)。











