APPROX_COUNT_DISTINCT 与 COUNT(DISTINCT) 的核心区别在于:前者基于 HyperLogLog 算法返回近似去重计数(误差率约 0.8%),速度快、内存占用低,适用于大数据实时分析;后者返回精确结果,但性能差、内存开销大,适用于强一致性场景。

APPROX_COUNT_DISTINCT 和 COUNT(DISTINCT) 的核心区别在哪?
APPROX_COUNT_DISTINCT 不是精确计数,而是基于 HyperLogLog 算法的近似去重计数,误差率通常在 0.8% 左右;而 COUNT(DISTINCT) 是确定性结果,但代价高、内存占用大、执行慢。两者语义不同:前者换速度和资源,后者保精度。
-
APPROX_COUNT_DISTINCT只接受单个表达式(如列名),不支持DISTINCT关键字嵌套或多个参数 - 它返回
bigint类型,和COUNT一致,但值不可用于主键校验、审计对账等强一致性场景 - 在大数据量(千万级以上)、实时分析类查询中,
APPROX_COUNT_DISTINCT常比COUNT(DISTINCT)快 3–10 倍,且内存峰值低得多
什么时候该用 APPROX_COUNT_DISTINCT?
适用场景非常明确:你只需要“够用的数字”,而不是“绝对正确的数字”。
- 实时看板里统计“今日独立访客数(UV)”,误差几百人不影响决策
- 数据探索阶段快速估算某字段去重基数,辅助判断是否值得建索引或分区
- ETL 预检脚本中验证源表关键字段的大概唯一性,避免全量跑
COUNT(DISTINCT)卡住调度 - 联合多个大表做宽表聚合时,嵌套
COUNT(DISTINCT)容易触发内存溢出,换成APPROX_COUNT_DISTINCT可绕过
不适用场景包括:
- 财务报表、合同数据、合规审计等要求零误差的输出
- 小于 10 万行的表——此时
COUNT(DISTINCT)很快,没必要引入近似逻辑 - 列值高度重复(比如状态码只有 3–5 个取值),近似算法反而可能不如精确计算稳定
实际写法和常见报错
基本用法很简单:
SELECT APPROX_COUNT_DISTINCT(UserID) AS approx_uv FROM dbo.Events;
但容易踩的坑不少:
- 不能写成
APPROX_COUNT_DISTINCT(DISTINCT UserID)—— 语法错误,DISTINCT关键字在这里非法 - 不支持表达式组合,例如
APPROX_COUNT_DISTINCT(Year(OrderDate) * 100 + Month(OrderDate))会报错,必须先计算好列或用子查询包裹 - 在视图或内联表值函数中使用时,如果底层表没有统计信息,优化器可能误判基数,导致后续 JOIN 或 FILTER 效率下降
- 与
GROUP BY混用没问题,但注意:它不能出现在HAVING子句的条件中作为筛选依据(因为不是确定值),比如HAVING APPROX_COUNT_DISTINCT(x) > 1000是允许的,但结果不可靠,慎用
兼容性和版本限制
APPROX_COUNT_DISTINCT 从 SQL Server 2019 (15.x) 开始正式支持,SQL Server 2022 当然可用,但需确认数据库兼容级别 ≥ 150(对应 SQL Server 2019)。如果数据库仍是兼容级别 140(SQL Server 2017),即使实例是 2022,该函数也会报“无法识别的内置函数”错误。
检查方式:
SELECT compatibility_level FROM sys.databases WHERE name = DB_NAME();
升级方式(以数据库名为 MyDB 为例):
ALTER DATABASE MyDB SET COMPATIBILITY_LEVEL = 150;
注意:升级兼容级别会影响整个查询优化器行为,建议先在测试环境验证关键查询计划是否变化。不是所有旧查询都会变好,个别 case 可能因新 Cardinality Estimator 导致计划退化。
真正要用好 APPROX_COUNT_DISTINCT,得先承认一个事实:它解决的从来不是“怎么算对”,而是“怎么算得又快又省”。精度让渡是主动选择,不是妥协。

















