PERCENT_RANK()可精准定位各部门前10%员工:它按部门分组、薪资降序计算0~1相对排名,pct_rank < 0.1即为前10%,再对外层过滤结果求平均即可。

用 PERCENT_RANK() 窗口函数定位前10%员工
PERCENT_RANK() 是最直接的工具:它为每个员工在部门内按薪资排序后,计算其相对排名(范围是 0 到 1,最高薪者为 0,最低薪者接近 1)。注意它不是“前 N 名”,而是按比例划分——所以 PERCENT_RANK() < 0.1 才对应前 10%。
常见错误是误用 ROW_NUMBER() 或 RANK():它们返回整数序号,无法直接表达“10%”这个比例概念;强行用 ROW_NUMBER() <= COUNT(<em>) </em> 0.1 还得先聚合再 JOIN,逻辑绕且易出错。
使用时必须搭配 PARTITION BY department 和 ORDER BY salary DESC,否则就变成全表排名,失去“每个部门”的前提。
SELECT department, salary,
PERCENT_RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS pct_rank
FROM employees;
过滤并计算部门内前10%的平均薪资
关键在于:不能先算全局平均再过滤,也不能在子查询里漏掉 PARTITION BY。正确做法是把 PERCENT_RANK() 放在子查询或 CTE 中生成排名,外层只对 pct_rank < 0.1 的行求平均。
-
PERCENT_RANK()对相同薪资的员工会给出相同排名值(比如两个并列第1名,都得到 0.0),这符合业务直觉(同薪同待遇) - 若某部门只有 3 人,
PERCENT_RANK()结果为 0.0、0.5、1.0,那么< 0.1只能选中 1 人(即最高薪者)——这是合理行为,不是 bug - 某些旧版 MySQL(< 8.0)不支持窗口函数,此时需改用变量模拟或应用层分页处理
WITH ranked AS (
SELECT department, salary,
PERCENT_RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS pct_rank
FROM employees
)
SELECT department, ROUND(AVG(salary), 2) AS avg_top_10pct_salary
FROM ranked
WHERE pct_rank < 0.1
GROUP BY department;
替代方案:用 NTILE(10) 分十分位但要注意陷阱
NTILE(10) 把每部门员工平均分成 10 组(编号 1–10),理论上第 1 组就是前 10%。但它强制“均分”,当部门人数不能被 10 整除时,低编号组会略多几人(例如 13 人会被分作 2,2,1,1,…),导致实际占比可能达 15% 或更高。
- 不推荐用于精确的“前 10%”场景,更适合“大致分层”
-
NTILE()的分组边界不可控,相同薪资可能被拆到不同组,破坏一致性 - 如果坚持用,必须加
ORDER BY salary DESC, employee_id避免非确定性分组
真正要严格按比例截断,PERCENT_RANK() 是唯一可靠选择。别被“NTILE 看起来更直观”带偏——它的设计目标是分桶,不是百分位筛选。

















