要从 employee(dept_id, emp_id, salary) 中查询每个部门工资最高的 2 名员工。下列核心思路正确的是()。
PARTITION BY dept_id 会让窗口序号在每个部门内重新从 1 开始,ORDER BY salary DESC 决定部门内排名。外层筛选 rn<=2,才能得到每个部门各自的前 2 名。
选项分析
错误。普通 GROUP BY 不能在不聚合的情况下稳定返回对应员工明细。
错误。它只返回全表工资最高的两行,不是每个部门两行。
正确。按部门分区、按工资降序编号,再筛选每组前两行。
错误。字符串排序会让数值大小关系失真,也没有解决分组排名。
本题为什么容易错
“分组”两个字很容易让人条件反射地选 GROUP BY。但本题不是只求每组最大值,还要保留前两名员工的整行信息,这正是窗口函数更顺手的地方。
简短答案
每个部门取工资最高的 2 名员工,为什么要用 PARTITION BY,正确答案是 C(使用 ROW_NUMBER() OVER(PARTITION BY dept_id ORDER BY salary DESC),再筛选序号不大于 2)。PARTITION BY dept_id 会让窗口序号在每个部门内重新从 1 开始,ORDER BY salary DESC 决定部门内排名。外层筛选 rn<=2,才能得到每个部门各自的前 2 名。
易混淆概念对比表
| 概念 | 本题判断 | 区别要点 | 记忆提示 |
|---|---|---|---|
| 仅按 dept_id 做 GROUP BY,并直接选择任意 emp_id | 本题干扰项 | 错误。普通 GROUP BY 不能在不聚合的情况下稳定返回对应员工明细。 | 看到该词不要急着选,先判断是否真正解决题干问题 |
| 全表按 salary 降序后只取前 2 行 | 本题干扰项 | 错误。它只返回全表工资最高的两行,不是每个部门两行。 | 看到该词不要急着选,先判断是否真正解决题干问题 |
| 使用 ROW_NUMBER() OVER(PARTITION BY dept_id ORDER BY salary DESC),再筛选序号不大于 2 | 本题正确答案 | 正确。按部门分区、按工资降序编号,再筛选每组前两行。 | 看到题干核心场景时优先联想到它 |
| 把 salary 转成字符串后按字典序取前 2 行 | 本题干扰项 | 错误。字符串排序会让数值大小关系失真,也没有解决分组排名。 | 看到该词不要急着选,先判断是否真正解决题干问题 |
本题易混淆选项怎么区分
- 仅按 dept_id 做 GROUP BY,并直接选择任意 emp_id:错误。普通 GROUP BY 不能在不聚合的情况下稳定返回对应员工明细。
- 全表按 salary 降序后只取前 2 行:错误。它只返回全表工资最高的两行,不是每个部门两行。
- 把 salary 转成字符串后按字典序取前 2 行:错误。字符串排序会让数值大小关系失真,也没有解决分组排名。
知识点详解
窗口函数在不折叠明细行的前提下计算排名、累计值和前后行信息。ROW_NUMBER 对每行给出唯一连续序号;RANK 遇到并列会跳号;DENSE_RANK 并列但不跳号。分组 Top N 的业务定义若不明确,并列边界可能直接改变返回行数。数据量较大时,还要结合分区列和排序列设计索引并查看执行计划。
备考速记
每组重排靠 PARTITION BY,组内先后靠 ORDER BY。
SQL 在分组Top N场景中的作用
SQL在本题中的核心价值,是解决“要从 employee(dept_id, emp_id, salary) 中查询每个部门工资最高的 2 名员工。下列核心思路正确的是()”这个场景问题。复习时不要只背选项名称,还要理解它为什么适用于该场景,以及它能解决哪类安全、流程或管理问题。
同类题怎么考
- 每个班级、部门或商品类别取前 N 条记录。
- 比较 ROW_NUMBER、RANK 与 DENSE_RANK 的并列处理。
SQL 在数据库系统工程师软考中的考法
软考选择题通常不会只考概念定义,还会把SQL放到分组Top N场景中,要求判断它的作用、适用范围或与相近概念的区别。遇到这类题时,先抓住题干中的业务场景,再看哪个选项最能解决该场景下的核心问题。
解题思路
先问清楚“前 2 名”是在全公司排,还是每个部门各排一次。选项 B 只会给出全公司两个人;PARTITION BY 才像把员工按部门分到不同教室,每个教室重新编号。ROW_NUMBER 得到序号后还要套一层查询筛 rn<=2,因为多数数据库不能在同一层 WHERE 里直接使用刚算出的窗口别名。
考点定位
GROUP BY 会把多行聚合成组结果,窗口函数保留明细行并在组内计算序号。题干既要员工明细又要每组排名时,优先考虑窗口函数。
易错提醒
- 漏写 PARTITION BY,结果变成全表统一排名。
- 工资并列时没有考虑 ROW_NUMBER、RANK、DENSE_RANK 的差异。
- 直接在同层 WHERE 使用窗口函数别名,忽略 SQL 执行阶段。
备考提示
- 先确认并列工资要固定两行,还是要把并列名次全部保留,再选择排名函数。
- 生产查询再补稳定排序字段,例如 `salary DESC, emp_id`,避免同薪顺序漂移。
你可能还想了解
- ROW_NUMBER 和 RANK 有什么区别?
- 窗口函数为什么不能直接写在 WHERE?
- GROUP BY 和 PARTITION BY 有什么区别?
本文小结
每个部门都要重新排名,因此用PARTITION BY dept_id划分窗口,并按salary降序生成ROW_NUMBER;外层筛选rn<=2即可保留每个部门的两条员工明细。