COUNT(DISTINCT column) 是最直接通用的去重计数写法,支持主流数据库,自动对指定列去重并计数,忽略NULL值;多列组合去重在MySQL/PostgreSQL中支持,SQL Server需改用子查询。

用 COUNT(DISTINCT column) 直接统计去重总数
这是最直接、最通用的解法,几乎所有主流 SQL 数据库(MySQL、PostgreSQL、SQL Server、Oracle、SQLite)都支持 COUNT(DISTINCT column)。它会在聚合前自动对指定字段去重,再计数。
注意:如果 column 中有 NULL,DISTINCT 会把所有 NULL 视为一个值(即只算一次),这符合 SQL 标准,但容易被误以为漏统计——其实不是漏,是设计如此。
示例:
SELECT COUNT(DISTINCT user_id) FROM orders;
- 适用于单字段去重计数,语义清晰、写法简洁
- 在大数据量下可能较慢(尤其无索引时),因为需构建临时唯一集合
- MySQL 5.7+ 和 PostgreSQL 支持多列组合去重,如
COUNT(DISTINCT status, region);但 SQL Server 不支持多列,会报错
GROUP BY + COUNT(*) 配合子查询也能实现,但没必要
有人会想到先 GROUP BY column 再外层套 COUNT(*),例如:
SELECT COUNT(*) FROM (SELECT DISTINCT category FROM products) AS t;
逻辑上等价,但多了一层嵌套,可读性差,且多数引擎无法优化掉这层开销。现代优化器一般能将 COUNT(DISTINCT) 转成更高效的执行计划,而子查询方式反而可能阻止优化。
- 仅在极少数旧版本数据库(如早期 SQLite)不支持
DISTINCT在聚合函数中时才考虑退化写法 - 若同时需要去重值列表和总数,可复用
DISTINCT子查询,但总数仍建议单独用COUNT(DISTINCT),避免重复扫描
NULL 值和空字符串是否算不同值?要看业务定义
COUNT(DISTINCT column) 把 NULL 当作一个独立值,但把空字符串 '' 和非空字符串明确区分开。如果你的业务里“空字符串”和“NULL”语义相同(比如都代表未填写),那直接统计会高估唯一值数量。
- 需要统一处理时,用
COUNT(DISTINCT COALESCE(column, ''))或COUNT(DISTINCT NULLIF(column, ''))显式归一化 - MySQL 中注意
''和' '(带空格)是不同值;PostgreSQL 对尾部空格敏感度取决于 collation,这点容易踩坑 - 字符集影响比较逻辑:UTF8MB4 下 emoji 或某些生僻字可能因排序规则导致“看起来一样却不算重复”
大数据量下 COUNT(DISTINCT) 可能 OOM 或超时
当目标字段基数极高(比如百亿级日志中的 user_agent),COUNT(DISTINCT) 可能吃光内存或触发磁盘临时表,甚至被数据库主动 kill。
- PostgreSQL 可设
work_mem提升哈希聚合能力,但治标不治本 - 真正海量场景应改用近似算法:PostgreSQL 用
APPROX_COUNT_DISTINCT()(v13+),BigQuery 用APPROX_COUNT_DISTINCT(),Spark SQL 用approx_count_distinct() - 如果只是做趋势监控,采样后估算(如
TABLESAMPLE SYSTEM (1))比硬扛全量更实际
COUNT(DISTINCT column);真遇到性能或语义歧义,再根据具体数据库版本和数据特征选应对策略。别为了“看起来高级”提前引入子查询或近似函数——多数时候,简单写法就是最稳的。

















