用ROW_NUMBER()窗口函数按部门分组排序取Top 1最可靠,因GROUP BY配MAX仅得最高薪而非对应员工;需保留行粒度,PARTITION BY dept分组、ORDER BY salary DESC,name ASC确保稳定性。

用窗口函数 ROW_NUMBER() 排序取 Top 1 最可靠
直接 GROUP BY dept 配 MAX(salary) 只能查出最高工资,拿不到对应员工姓名——这是初学者最常踩的坑。真正要“查出人”,必须保留原始行粒度,靠窗口函数给每组内工资排序。ROW_NUMBER() 比 RANK() 更稳妥:即使同部门多人并列最高薪,也能强制选一个(按姓名或 ID 补充排序可控制选谁)。
示例写法:
SELECT dept, name, salary
FROM (
SELECT dept, name, salary,
ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary DESC, name ASC) AS rn
FROM employees
) t
WHERE rn = 1;
-
PARTITION BY dept是分组依据,不是GROUP BY,不聚合,保留所有行 -
ORDER BY salary DESC, name ASC确保相同工资时结果稳定(否则ROW_NUMBER()顺序可能随执行变化) - MySQL 8.0+、PostgreSQL、SQL Server、Oracle 都支持;SQLite 3.25+ 也支持
旧版本 MySQL(
没有窗口函数时,得靠关联查“不存在更高工资的同部门员工”来定义“最高”。逻辑是:对某员工 A,找不到同部门 salary > A.salary 的人,则 A 就是最高薪。
自连接写法(较易理解):
SELECT e1.dept, e1.name, e1.salary FROM employees e1 LEFT JOIN employees e2 ON e1.dept = e2.dept AND e1.salary < e2.salary WHERE e2.dept IS NULL;
- 注意是
e1.salary ,不是 <code>,否则会漏掉唯一最高薪的情况 - 如果存在多人并列最高薪,此方法会全部返回;而
ROW_NUMBER()默认只返回一行 - 性能上,大表需确保
(dept, salary)有复合索引,否则慢得明显
用 IN 子查询容易漏数据
常见错误写法:WHERE (dept, salary) IN (SELECT dept, MAX(salary) FROM employees GROUP BY dept)。表面看没问题,但实际在部分数据库(如老版本 MySQL)中,(a,b) IN ((x,y),...) 元组比较可能不被支持或行为异常;更严重的是,如果 salary 字段为 NULL,整个条件失效——NULL = anything 永远为 UNKNOWN,导致该部门没人被查出来。
- 务必先确认
salary是否允许为NULL,若允许,得额外加AND salary IS NOT NULL - 即便语法通过,某些引擎对多列
IN优化差,执行计划可能全表扫描 - 不如改用
EXISTS或窗口函数,语义更清晰、兼容性更好
部门为空或数据类型不一致会导致结果意外为空
真实业务里,dept 字段经常是字符串,但可能混入空格、不可见字符(如 \t)、全角空格,甚至 NULL。这时 PARTITION BY dept 会把 NULL 单独分一组,而你可能根本没意识到还有“无部门”员工;同样,如果有的 dept 是 '研发部 '(尾部空格),有的是 '研发部',它们会被当两个部门处理。
- 查之前先运行
SELECT dept, COUNT(*) FROM employees GROUP BY dept ORDER BY COUNT(*) DESC,肉眼扫一遍异常值 - 清洗建议:统一用
TRIM(dept),必要时加COALESCE(dept, '未知部门') - 别跳过这步——线上跑着跑着突然某个部门“消失”了,八成是这个原因

















