用MAX() OVER(PARTITION BY department_id)可单次扫描获取各部门最高薪,避免子查询和GROUP BY,需配合索引idx_dept_salary提升性能。

直接用 MAX() OVER() 就行,不需要子查询,也不需要 GROUP BY 合并行。
为什么不用子查询或关联也能拿到部门最高薪
传统写法常靠子查询先算出每个部门的 MAX(salary),再和主表 JOIN 匹配;或者用相关子查询逐行查。这两种方式在数据量大时容易触发全表扫描、临时表、文件排序。而 MAX() OVER(PARTITION BY department_id) 是单次扫描完成:MySQL 8.0 在执行时会按 department_id 分区,在每个分区内直接计算最大值,并把结果“广播”到该分区每一行——保留原始行结构,不丢失员工信息。
- 必须有合适的索引,比如
KEY idx_dept_salary (department_id, salary),否则OVER(PARTITION BY ... ORDER BY ...)可能退化成 filesort -
MAX() OVER()不依赖ORDER BY,但加上ORDER BY salary DESC会影响窗口范围语义(如配合ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW),纯求最大值时可省略 - 如果表里有 NULL 的
salary,MAX()会自动忽略,行为和聚合函数一致
正确写法:一行 MAX() OVER() + 分区字段
假设表结构为 employee(id, name, salary, department_id),要查每个员工所在部门的最高薪资:
SELECT id, name, salary, department_id, MAX(salary) OVER (PARTITION BY department_id) AS dept_max_salary FROM employee;
- 结果中每行都带本部门的最高薪,可用于后续过滤(比如只看“等于部门最高薪”的人)或计算占比(
salary / MAX(salary) OVER(...)) - 别名
dept_max_salary必须用AS显式声明,否则在外部 WHERE 或 ORDER BY 中无法引用(窗口函数结果不能直接在同级 WHERE 中使用) - 若需同时显示部门平均薪、人数等,可叠加多个窗口函数:
AVG(salary) OVER(...)、COUNT(*) OVER(...),共用一个分区定义,无额外开销
常见错误:混淆窗口函数和聚合函数的执行层级
典型翻车现场:
-- ❌ 报错:'Invalid use of group function' SELECT *, MAX(salary) OVER (PARTITION BY department_id) FROM employee WHERE salary = MAX(salary) OVER (PARTITION BY department_id); <p>-- ✅ 正确:用子查询或 CTE 包一层 SELECT <em> FROM ( SELECT </em>, MAX(salary) OVER (PARTITION BY department_id) AS dept_max_salary FROM employee ) t WHERE salary = dept_max_salary;
- WHERE 执行早于窗口函数计算,所以不能直接在 WHERE 里调用
MAX() OVER() - ORDER BY 中可以安全使用窗口函数别名,因为它是最后执行的阶段
- 如果业务只要“部门最高薪那条记录”,且存在多人并列最高,
ROW_NUMBER() OVER(...)或RANK()更合适——MAX() OVER()只给数值,不标识“是谁”
真正难的不是写对语法,而是想清楚你要的是“每个部门的最高薪资数值”,还是“拿最高薪资的那个人”。前者用 MAX() OVER() 一步到位;后者得搭配排名函数或去重逻辑,别混用。


















