最直接做法是用相关子查询:外层查员工,内层按相同部门计算max(salary),再用e1.salary = (select max(e2.salary) from employees e2 where e2.dept_id = e1.dept_id)匹配,可返回所有并列最高者;若需每部门仅一人,则应使用row_number()窗口函数配合多字段排序。

子查询里用 WHERE 匹配部门最大工资值
最直接的做法是:先在子查询中算出每个部门的最高工资,再在外层查出工资等于该值的员工。但要注意——多个员工可能并列最高,这种写法会全部返回,不是“只取一个”。
- 子查询必须关联部门字段,否则会变成全表最大值,所有部门都套用同一个数
- 写成
(SELECT MAX(salary) FROM employees e2 WHERE e2.dept_id = e1.dept_id)才能按部门分别计算 - 如果表里有
dept_id为空的记录,这部分会被排除(NULL不参与=判断)
SELECT name, dept_id, salary FROM employees e1 WHERE salary = ( SELECT MAX(salary) FROM employees e2 WHERE e2.dept_id = e1.dept_id );
ROW_NUMBER() 窗口函数比子查询更可控
当需要“每个部门只取 1 个最高工资员工”(比如按姓名或 ID 排序后取第一个),ROW_NUMBER() 是更稳妥的选择。子查询无法天然处理“并列时选谁”的逻辑。
-
ROW_NUMBER()保证每行唯一编号,即使工资相同也能按指定顺序排开 - 排序字段建议加上
id或name作为次级条件,避免结果不稳定 - MySQL 8.0+、PostgreSQL、SQL Server 都支持;SQLite 3.25+ 也支持,但旧版不支持窗口函数
SELECT name, dept_id, salary
FROM (
SELECT name, dept_id, salary,
ROW_NUMBER() OVER (
PARTITION BY dept_id
ORDER BY salary DESC, id ASC
) AS rn
FROM employees
) ranked
WHERE rn = 1;
用 IN 替代 = 会导致意外结果
有人试图用 IN 写成 WHERE (dept_id, salary) IN (SELECT dept_id, MAX(salary) ...),这看似合理,但多数数据库(如 MySQL)不支持多列 IN 子查询与复合主键语义对齐,实际执行可能报错或漏数据。
- PostgreSQL 支持
(a,b) IN (SELECT a,b ...),但 MySQL 直到 8.0.19 才部分支持,且要求子查询明确返回两列 - 即使语法通过,若子查询未
GROUP BY dept_id,结果集可能少于部门数,导致外层查不到人 - 更安全的做法是用
JOIN或窗口函数,而不是依赖多列IN
性能差异:子查询 vs 窗口函数
数据量小时没区别;但上万行后,子查询方式可能触发对每个员工重复执行一次内部聚合(即“相关子查询”),而窗口函数只需扫描一次表。
- 检查执行计划时,留意是否有
DEPENDENT SUBQUERY—— 出现就说明子查询被反复调用 - 如果部门数极少(比如就 5 个),可以先用
CREATE TEMP TABLE缓存各部最高工资,再JOIN,反而更快 - 确保
(dept_id, salary)有联合索引,能加速子查询里的MAX()和外层匹配











