PERCENT_RANK()按(rank−1)/(total_rows−1)计算,返回0.0至1.0(总行数≥2时末行为1.0),首行恒为0.0,须用OVER()窗口语法,不可用于WHERE或GROUP BY,MySQL 8.0+支持。

PERCENT_RANK函数的计算逻辑和返回值范围
PERCENT_RANK() 不是简单地把名次除以总数,而是按公式 (rank - 1) / (total_rows - 1) 计算。这意味着:第一行永远返回 0.0,最后一行永远返回 1.0(只要总行数 ≥ 2),中间值是线性插值结果。
它必须配合 OVER() 子句使用,且仅支持窗口函数语法——不能出现在 WHERE 或 GROUP BY 中。在 MySQL 8.0+ 才可用,5.7 及更早版本会报错 FUNCTION xxx.PERCENT_RANK does not exist。
基础用法:按单字段排序并计算全局百分比排位
最常见场景是给销售记录按金额从高到低排位,并算出每个订单处于整体分布的什么位置:
SELECT order_id, amount, PERCENT_RANK() OVER (ORDER BY amount DESC) AS pct_rank FROM orders;
注意点:
-
ORDER BY决定排序方向,DESC表示“金额越高,排位越靠前”,对应PERCENT_RANK值越小 - 如果
amount有重复值,它们会获得相同RANK(跳过后续名次),但PERCENT_RANK仍按实际行号位置计算分母 - 空值(
NULL)默认排在最后(DESC时),若想让NULL排最前,需显式写ORDER BY amount DESC NULLS FIRST(MySQL 8.0.22+ 支持)
分区计算:按类别分组后各自算百分比排位
比如要分别看「华东」和「华南」区域的订单金额在本区内排多少百分位,就得用 PARTITION BY:
SELECT
region,
order_id,
amount,
PERCENT_RANK() OVER (
PARTITION BY region
ORDER BY amount DESC
) AS pct_rank_in_region
FROM orders
WHERE region IN ('华东', '华南');关键细节:
- 每个
PARTITION独立计数,所以华东组的1.0和华南组的1.0没有可比性 -
PARTITION BY列必须出现在SELECT或WHERE中才便于理解结果;否则容易误以为是全表统计 - 若某分区只有 1 行,
PERCENT_RANK()返回0.0(因分母为1-1=0,MySQL 定义此时结果为 0)
和CUME_DIST、RANK的区别与选型建议
三个函数常被混淆,但语义不同:
-
RANK()返回整数名次(重复值同名次,跳号),不归一化 -
CUME_DIST()计算“≤当前值的行数占比”,结果范围仍是[1/N, 1.0],且重复值共享同一累积比例 -
PERCENT_RANK()强调相对位置的线性分布,首尾固定为0/1,更适合做分位图或阈值筛选(如取前 10%:PERCENT_RANK() < 0.1)
真正容易被忽略的是:当数据量小(比如某分区仅 2–3 行)时,PERCENT_RANK() 的离散性很强——0、0.5、1.0 这类值频繁出现,直接按 < 0.2 截取可能一条记录都捞不到。这时候得结合业务判断,是否该换用 NTILE(5) 做五等分更稳妥。


















