相关子查询能准确查出每个部门工资等于本部门最高工资的员工,避免全公司最大值误匹配、group by非分组字段报错、exists无法数值比较等问题,但需注意null和部门为空的边缘情况。

用相关子查询找每个部门最高工资的员工
直接用 SELECT 配合相关子查询就能拿到结果,关键不是“查最高工资”,而是“查工资等于本部门最高工资的员工”。否则会漏掉同薪多人、或误匹配其他部门数据。
常见错误是写成:WHERE salary = (SELECT MAX(salary) FROM employees)——这查的是全公司最高工资,不是每个部门的。
- 必须在子查询中关联外层部门字段,比如
e1.department_id = e2.department_id - 子查询要放在
WHERE条件里,且返回单值(MAX()满足) - 如果一个部门有多个员工并列最高工资,都会被查出来——这是正确行为,不是 bug
SELECT e1.name, e1.department_id, e1.salary FROM employees e1 WHERE e1.salary = ( SELECT MAX(e2.salary) FROM employees e2 WHERE e2.department_id = e1.department_id );
为什么不能用 GROUP BY + MAX() 直接联表?
因为 GROUP BY department_id 后,非分组字段(如 name)无法直接选取,MySQL 5.7+ 默认报错 Expression #1 of SELECT list is not in GROUP BY clause;即使开宽松模式,name 值也是不确定的随机行。
有人试过 JOIN 子查询结果,但容易出错:
- 子查询只返回
department_id和MAX(salary),没带id或name,联表后仍无法定位具体员工 - 若用
IN匹配(department_id, salary)元组,需确保字段类型完全一致(比如DECIMAL和FLOAT比较可能失准) - 相关子查询虽稍慢,但语义清晰、无需担心重复键或精度问题
性能和可读性权衡:EXISTS 能替代吗?
不能。 EXISTS 适合“是否存在”,不适用于“取某个极值对应的记录”。你没法用 EXISTS 表达“当前员工工资是否等于本部门最高工资”这个逻辑,因为 EXISTS 子查询只能返回真假,不能参与数值比较。
有人误写成:
WHERE EXISTS (
SELECT 1 FROM employees e2
WHERE e2.department_id = e1.department_id
AND e2.salary > e1.salary -- 这查的是“有更高工资的人”,反向逻辑难读且易错
)
- 这种写法实际查的是“非最高工资员工”,得再套一层
NOT EXISTS,可读性陡降 - 一旦存在
NULL工资,>比较结果为UNKNOWN,导致该员工被意外排除 - 原生相关子查询更贴近业务表述,也更容易加索引优化(建议在
(department_id, salary)上建联合索引)
注意 NULL 和多值边缘情况
如果某部门所有员工 salary 都是 NULL,MAX(salary) 返回 NULL,而 salary = NULL 永远为 UNKNOWN,该部门结果为空——这是 SQL 三值逻辑决定的,不是 bug,但常被忽略。
- 需要包含 NULL 工资员工时,得显式判断:
WHERE e1.salary = (...) OR (e1.salary IS NULL AND ...),但通常业务上工资不允许为 NULL - 若部门字段本身为
NULL(比如未分配部门),这些员工会被归为同一组,MAX()仍生效,但语义可能不符合预期 - 子查询中别名(如
e2)必须与外层不同,否则会引发列解析歧义
相关子查询看着绕,但它把“对每行动态计算条件”这件事表达得最直白;真正容易出问题的,是忽略 NULL 行为、部门字段为空、以及没意识到 MAX() 在空集下返回 NULL。











