PERCENT_RANK() 不能直接筛选中位数以上记录,因其返回相对排名而非原始值,无法精确对应数学中位数,且在小样本、重复值、偶数行等场景下结果不可靠。

PERCENT_RANK() 不能直接用来筛选中位数以上记录
因为 PERCENT_RANK() 返回的是相对排名(0 到 1 的比例),不是原始值本身,也不等于数学中位数。比如数据 [1,2,3,4],PERCENT_RANK() 给出的是 [0, 0.33, 0.66, 1],没有一行恰好是 0.5;而中位数是 2.5,真正“大于中位数”的只有 3 和 4。直接写 WHERE PERCENT_RANK() OVER (...) > 0.5 会漏掉边界、误判重复值,且在小样本下完全不可靠。
为什么用 PERCENT_RANK() > 0.5 会出错
常见错误现象包括:
-
PERCENT_RANK()在分组内最小值恒为 0,最大值恒为 1,中间值按(rank - 1) / (cnt - 1)算——当组内只有 2 行时,结果只能是 0 或 1,永远没有 0.5 - 若存在大量重复值(如
[5,5,5,5]),所有行的PERCENT_RANK()都是 0,> 0.5结果为空,但中位数明明是 5,大于它的记录理应为空——这个结果碰巧对了,但逻辑不成立 - ORDER BY 缺失或用常量(如
ORDER BY 1)会导致数据库返回全 0 或报错,MySQL 8.0+ 可能静默返回错误值 - 它无法处理偶数行插值需求:中位数是中间两数平均,而
PERCENT_RANK()没有提供这两个原始值的索引
想用 PERCENT_RANK() 近似,必须加容差且仅限大样本
如果你只是做快速探索、不要求精确数学定义,可以这样写(仅限 PostgreSQL/SQL Server/Oracle):
SELECT group_id, value
FROM (
SELECT group_id, value,
PERCENT_RANK() OVER (PARTITION BY group_id ORDER BY value) AS pr
FROM t
) t2
WHERE pr > 0.5 - 0.01 AND pr <p>但要注意:</p>
- 容差
0.01是拍脑袋定的,样本越小越不准 - 仍需
ORDER BY value,且value不能为NULL(否则该行被跳过,pr分母变小,结果偏移) - 无法区分
[1,1,3,3](中位数=2,>2 得到 3,3)和[1,2,3,4](中位数=2.5,>2.5 得到 3,4)——PERCENT_RANK()对这两组输出完全一样 - MySQL 8.0 支持
PERCENT_RANK(),但不支持后续过滤逻辑跨组对齐,容易误把全表排名当成分组排名
真正可靠的做法:先算中位数值,再比较
所有数据库都适用的底线方案是:不用 PERCENT_RANK() 做判断依据,只用它辅助理解分布;中位数本身必须基于原始值计算。推荐组合:
- PostgreSQL/SQL Server/Oracle:用
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY value)算出每组中位数,再CROSS JOIN或LATERAL关联原表过滤 - MySQL 8.0+:用两层
ROW_NUMBER()+COUNT(*) OVER()定位中间位置行,取平均后作为阈值 - 任何数据库:避免在
WHERE中调用窗口函数,中位数必须是标量(单个值),不是每行一个动态排名
最易被忽略的一点:中位数以上 ≠ 排名在后 50%,而是「严格大于该组中位数值」。这个“值”必须显式算出来,不能靠排名近似替代。










