因为COUNT(DISTINCT)是聚合函数,而OVER()仅接受窗口函数,二者语义互斥;主流数据库均报“aggregate function calls cannot be nested”错误,非实现限制而是SQL标准禁止。

为什么COUNT(DISTINCT) OVER()直接报错?
因为 COUNT(DISTINCT) 本质是聚合函数,而 OVER() 只接受窗口函数——这两类函数在 SQL 语义层面互斥。你写 COUNT(DISTINCT user_id) OVER (PARTITION BY dept),PostgreSQL、MySQL 8+、SQL Server、Oracle 全都会报类似错误:ERROR: aggregate function calls cannot be nested。这不是实现偷懒,而是标准限制:窗口上下文里不能触发“先分组再聚合”的计算路径。
MySQL 8+ 也不支持,别被语法误导
MySQL 8+ 支持 COUNT(DISTINCT col) 作为普通聚合,也支持 SUM() OVER() 这类纯窗口函数,但它**同样拒绝** COUNT(DISTINCT col) OVER()。有人误以为 MySQL “更先进”,其实是混淆了两个能力:多列去重(COUNT(DISTINCT a, b))和窗口去重(COUNT(DISTINCT a) OVER())完全无关。前者是 MySQL 特有语法糖,后者所有主流数据库都禁用。
替代方案选哪个,取决于你的数据库和场景
PostgreSQL 推荐用 array_agg + unnest + cardinality 模拟,但必须带 ORDER BY 和显式 DISTINCT;SQL Server 或 Oracle 必须走子查询路线:SELECT COUNT(*) FROM (SELECT DISTINCT user_id FROM t WHERE ...) AS tmp;Spark/Hive 可用 SIZE(COLLECT_SET(user_id) OVER (PARTITION BY city)),但注意 COLLECT_SET 会自动去重且忽略 NULL,行为和标准 COUNT(DISTINCT) 不完全一致。
最容易被忽略的性能陷阱
用 array_agg 模拟累计去重时,窗口越大,数组越长,unnest 和 DISTINCT 开销呈线性增长;如果没在排序字段上建索引,ORDER BY event_time 可能触发全表排序;而子查询方案虽然写法啰嗦,但在大表 + 高频查询下反而更稳定——因为优化器能对 SELECT DISTINCT 单独做索引跳扫或哈希去重,不依赖窗口帧大小。

















