<p>PERCENT_RANK() 是按位置计算的相对比例,公式为 (rank - 1) / (total_rows - 1),结果恒在 0.0 到 1.0 之间;首行恒为 0.0,末行恒为 1.0(n > 1),并列值共享 rank,窗口函数不可用于 WHERE,须用子查询或 CTE;漏写 PARTITION BY 会导致跨组错误排名;ORDER BY 方向与 NULL 处理影响业务语义。</p>

PERCENT_RANK() 不是“第几百分位”,而是按位置算出来的相对比例,公式固定为 (rank - 1) / (total_rows - 1),结果恒在 0.0 到 1.0 之间。
PERCENT_RANK() 的计算公式和边界值怎么来的?
它不看数值大小分布,只依赖排序后的位置索引和窗口总行数:
- 首行
rank = 1→(1 - 1) / (n - 1) = 0.0,无论n多大,永远是 0.0 - 末行
rank = n→(n - 1) / (n - 1) = 1.0,但仅当n > 1;若窗口只有 1 行,分母为 0,SQL Server 和 PostgreSQL 返回NULL,Databricks 返回0 - 并列值共享同一
rank(即用RANK()值),所以它们的PERCENT_RANK()完全相同
例如数据 [100, 95, 95, 80] 升序排后 rank 是 [1, 2, 2, 4],对应 PERCENT_RANK() 就是 [0.0, 0.333, 0.333, 1.0]。
为什么不能直接在 WHERE 中用 PERCENT_RANK()?
窗口函数不能出现在 WHERE、GROUP BY 或 HAVING 中,否则报错:ERROR: window functions are not allowed in WHERE。
- 必须先用子查询或 CTE 算出
PERCENT_RANK(),再在外层过滤 - 错误写法:
WHERE PERCENT_RANK() OVER (ORDER BY score) > 0.9 - 正确写法:
SELECT user_id, score FROM ( SELECT user_id, score, PERCENT_RANK() OVER (ORDER BY score DESC) AS pct_rank FROM users WHERE status = 'active' ) t WHERE pct_rank >= 0.9;
- 漏掉外层
WHERE过滤,会导致COUNT(*)基数不准,排名失真
PARTITION BY 漏写会导致什么问题?
这是最高频误用:想查“每个部门薪资在本部门内的排名”,却只写 PERCENT_RANK() OVER (ORDER BY salary) —— 实际算的是全公司统一排名。
- 没加
PARTITION BY dept,所有员工共用一个分母(全公司人数 − 1) - HR 部门唯一员工可能拿到
0.92,其实是全公司前 8%,不是 HR 内前 8% - 正确写法必须显式分区:
SELECT dept, name, salary, PERCENT_RANK() OVER (PARTITION BY dept ORDER BY salary) AS pct_rank FROM employees;
- 分区字段
dept必须出现在SELECT或GROUP BY中,否则可能逻辑错乱或报错
ORDER BY 方向和 NULL 怎么影响结果?
排序方向决定“高分是否代表表现好”,而 NULL 默认排最前(ASC)或最后(DESC),会悄悄改变分母和分子。
- 用
ORDER BY score DESC时,高分排前面 →PERCENT_RANK()小才代表“表现好”;反向则相反,容易误读 -
NULL值默认参与计算:升序时排最前(rank = 1→PERCENT_RANK = 0.0),可能把无效数据算成“最优” - 想排除
NULL,得提前加WHERE score IS NOT NULL;想让它排最后,得写ORDER BY score DESC NULLS LAST - 业务上若需“严格前 5%”,应写
PERCENT_RANK() >= 0.95,而不是——它返回的是“比多少比例的人小”,不是“前多少比例”
真正容易被忽略的是:它对并列值不做任何特殊处理,也不调整分母,只机械套公式。一旦数据里有大量重复值或空值,又没提前清洗或指定 NULLS LAST,算出来的 0.0 和 1.0 可能根本不符合业务直觉。

















