如何在不使用JOIN的情况下实现按分组去重计数与全局去重计数的占比计算
解决方法:无需JOIN实现全局唯一值占比统计
确实很多主流SQL引擎(比如MySQL 8.x之前的版本、部分数据库分支)不支持在窗口函数中直接使用COUNT(DISTINCT),不过我们可以通过嵌套窗口函数+子查询的方式绕开这个限制,完全不需要使用JOIN。下面给你两种实用的方案:
方案一:利用ROW_NUMBER标记全局唯一值
SELECT id, COUNT(DISTINCT value) AS distinct_count, SUM(CASE WHEN rn = 1 THEN 1 ELSE 0 END) OVER () AS total_distinct, ROUND(COUNT(DISTINCT value) / SUM(CASE WHEN rn = 1 THEN 1 ELSE 0 END) OVER (), 3) AS percentage FROM ( -- 给每个value的首次出现标记rn=1,重复出现的标记为大于1的数 SELECT id, value, ROW_NUMBER() OVER (PARTITION BY value ORDER BY id) AS rn FROM have ) t GROUP BY id;
逻辑说明:
- 内层子查询通过
ROW_NUMBER() OVER (PARTITION BY value)给每个重复的value分配序号,只有第一次出现的value会得到rn=1。 - 外层查询用
SUM(CASE WHEN rn=1 THEN 1 ELSE 0 END) OVER ()统计所有rn=1的数量,也就是全局唯一value的总数(对应你要的total_distinct=6)。 - 最后按
id分组统计每个分组的唯一值数量,再计算占比并保留三位小数。
方案二:用DENSE_RANK获取全局唯一值总数
WITH distinct_pairs AS ( -- 先提取所有不重复的(id, value)组合 SELECT DISTINCT id, value FROM have ), ranked_values AS ( SELECT id, value, -- 给每个唯一的value分配连续排名 DENSE_RANK() OVER (ORDER BY value) AS value_rank FROM distinct_pairs ) SELECT id, COUNT(value) AS distinct_count, MAX(value_rank) OVER () AS total_distinct, ROUND(COUNT(value) / MAX(value_rank) OVER (), 3) AS percentage FROM ranked_values GROUP BY id;
逻辑说明:
- 第一个CTE
distinct_pairs先过滤掉每个id下重复的value,得到所有唯一的(id, value)对。 - 第二个CTE
ranked_values用DENSE_RANK()给每个全局唯一的value分配连续的排名,全局最大的排名值就是所有唯一value的总数。 - 最后按
id分组统计每个分组的唯一值数量,结合全局总数计算占比。
这两种方案都能完美输出你期望的结果,而且兼容性很好,支持绝大多数现代SQL引擎(MySQL 8+、PostgreSQL、SQL Server、Oracle等)。
内容的提问来源于stack exchange,提问作者Grizzly2501
相关产品推荐
相关产品推荐

