用ROW_NUMBER()按部门工资排序取Top 1最稳妥,但仅返回一个员工;需全量返回并列最高者时应改用RANK();MySQL 5.7及以下须用NOT EXISTS相关子查询,并注意NULL、索引与数据类型问题。

用窗口函数 ROW_NUMBER() 排序取 Top 1 最稳妥
直接 GROUP BY dept 再 MAX(salary) 只能拿到最高工资数值,拿不到对应员工姓名——这是新手最常卡住的地方。真正要查“谁拿最高工资”,得给每个部门内的员工按工资排序,再筛出序号为 1 的那条记录。
推荐用 ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary DESC):它会在每个部门内独立编号,相同工资也不会并列(避免漏人),且性能好、兼容主流数据库(PostgreSQL / SQL Server / MySQL 8.0+ / Oracle)。
注意:如果一个部门有多个员工工资并列最高,ROW_NUMBER() 只会随机选一个;需要全量返回并列者,得换 RANK() 或 DENSE_RANK()(见下一条)。
处理并列最高工资:改用 RANK() 而不是 ROW_NUMBER()
当部门里出现两个员工都拿 15000 元,而其他人低于这个数时,ROW_NUMBER() 会给其中一人标 1、另一人标 2,导致后者被过滤掉;RANK() 则会同时标 1,再跳到 3(即:1,1,3…),这样 WHERE rk = 1 就能抓全。
实操建议:
- 明确业务需求:只要一个代表?用
ROW_NUMBER();必须列出所有最高者?用RANK() -
RANK()和DENSE_RANK()区别在于并列后是否跳号(DENSE_RANK()是 1,1,2…),但对“取最高”场景二者效果一致 - MySQL 5.7 及更早版本不支持窗口函数,必须用自连接或子查询(性能差、易出错,不推荐)
MySQL 5.7 或低版本兼容写法:相关子查询 + NOT EXISTS
如果数据库不支持窗口函数(比如老项目还在用 MySQL 5.7),可用相关子查询模拟“部门内无人工资更高”这一逻辑:
SELECT e1.name, e1.dept, e1.salary FROM employee e1 WHERE NOT EXISTS ( SELECT 1 FROM employee e2 WHERE e2.dept = e1.dept AND e2.salary > e1.salary );
这个写法语义清晰,但要注意:
- 表要有复合索引
(dept, salary),否则性能急剧下降(尤其数据量大时) - 如果存在
salary IS NULL,e2.salary > e1.salary判断会失效,需额外加AND e2.salary IS NOT NULL - 不能用
IN或JOIN替代,容易因重复值或 NULL 导致结果错误
别忽略 NULL 和空部门的边界情况
实际数据中,dept 字段可能为空,或某个部门没人——这些都会让查询结果和预期不符。
检查点:
- 确认
dept是否允许为NULL:WHERE dept IS NOT NULL建议显式加上,避免把 NULL 当成一个“部门”参与分组 - 如果想包含空部门(显示“无员工”),就得用
LEFT JOIN关联部门表,而不是只查员工表 - 工资字段是
DECIMAL还是FLOAT?浮点类型比较可能有精度误差,ORDER BY salary DESC时建议先CAST(salary AS DECIMAL)
真正难的不是写出语法正确的 SQL,而是想清楚“最高工资员工”在业务里到底指什么:是唯一一人?允许并列?要不要排除离职状态?这些细节没对齐,再漂亮的窗口函数也救不了结果偏差。

















