rank()与dense_rank()根本区别在于并列值处理:rank()跳过后续名次,dense_rank()连续编号;如分数95、95、88,前者返回1、1、3,后者返回1、1、2。

为什么 rank() 和 dense_rank() 在同一数据上结果不同
根本区别在于对并列值的处理逻辑:rank() 会跳过后续名次,dense_rank() 则连续编号。比如三个人分数为 95、95、88,rank() 返回 1、1、3,dense_rank() 返回 1、1、2。
实际选型要看业务规则:排行榜要体现“第几名”且允许并列后空位(如奥运奖牌榜),用 rank();若要求 Top 3 必须取满三人(哪怕分数相同),就得用 dense_rank() 或配合 row_number() + 过滤条件。
-
row_number()严格按排序顺序给唯一序号,不处理并列,适合“取前 N 条记录”这类无歧义场景 - 所有窗口函数必须搭配
OVER子句,且ORDER BY是必需项,漏写会报错Window 'w' lacks ORDER BY clause - MySQL 8.0+ 才支持,低版本直接执行会提示
FUNCTION xxx does not exist
怎样用窗口函数实现“每个部门 Top 3 员工”
关键在 PARTITION BY —— 它把数据按部门分组,再在每组内独立计算排名。错误做法是先 ORDER BY dept_id, salary DESC 再套 LIMIT,那只会返回全局前 3,不是每部门前 3。
正确写法是子查询或 CTE 中嵌套窗口函数:
SELECT dept_id, name, salary, rn
FROM (
SELECT dept_id, name, salary,
ROW_NUMBER() OVER (PARTITION BY dept_id ORDER BY salary DESC) AS rn
FROM employees
) t
WHERE rn <= 3;
-
PARTITION BY dept_id必须写在OVER里,不能放到外部GROUP BY中——窗口函数不改变行数,GROUP BY会聚合掉细节 - 如果部门内有薪资并列,
ROW_NUMBER()可能导致“第 3 名”被随机人选中;此时应换用RANK(),再加一层去重或业务兜底 - 注意
ORDER BY里最好包含唯一字段(如id)避免 MySQL 8.0.20+ 之后因优化器变更导致结果不稳定
遇到“Only full group by”错误时怎么修
这不是窗口函数专属问题,但常在组合 GROUP BY 和窗口函数时触发。典型场景:想同时查部门平均薪资和该部门内员工排名,却写了 SELECT dept_id, AVG(salary), RANK() OVER (...) FROM ... GROUP BY dept_id。
错误本质是 MySQL 要求 SELECT 列要么在 GROUP BY 中,要么被聚合函数包裹。窗口函数不能替代聚合逻辑。
- 拆成两步:先用窗口函数算排名,再用外层聚合查均值;或用 JOIN 关联聚合结果
- 禁止在同一个
SELECT中混用未聚合列 +GROUP BY+ 窗口函数(除非该列也在GROUP BY里) - 临时关闭
sql_mode不解决问题,只是掩盖逻辑缺陷
性能差得明显?检查这几个地方
窗口函数本身不慢,但不当用法会让执行计划退化成全表扫描或临时表堆积。最常见瓶颈是 PARTITION BY 字段无索引,或 ORDER BY 涉及函数/表达式。
- 确保
PARTITION BY和ORDER BY的字段都有合适索引,例如(dept_id, salary)复合索引能同时支撑PARTITION BY dept_id ORDER BY salary DESC - 避免在
ORDER BY里用UPPER(name)这类函数,会导致索引失效;如需大小写不敏感排序,建函数索引(MySQL 8.0.13+)或预计算列 - 大表慎用
NTILE(100)这类分桶操作——它需要完整扫描并排序所有行才能切分,内存压力远高于RANK()
真正难的不是语法,是判断该用哪个函数、在哪加索引、以及要不要把窗口逻辑移到应用层做分页缓存。这些权衡点,调试时一眼看不出来。


















