在SQL数据处理的实际业务场景里,查询各部门薪资前三名是人力资源、财务部门常提出的需求,传统的子查询或者自连接方式不仅代码逻辑复杂,执行效率也不够理想,使用ROW_NUMBER窗口函数可以简洁高效地完成这个需求。

一、准备工作:创建测试数据表
首先我们需要创建一个员工薪资表,用于存储员工所属部门、员工姓名和薪资信息,方便后续演示查询逻辑,建表语句如下:
-- 创建员工薪资表
CREATE TABLE employee_salary (
id INT PRIMARY KEY AUTO_INCREMENT,
dept_name VARCHAR(50) NOT NULL COMMENT '部门名称',
emp_name VARCHAR(50) NOT NULL COMMENT '员工姓名',
salary DECIMAL(10,2) NOT NULL COMMENT '员工薪资'
);
-- 插入测试数据
INSERT INTO employee_salary (dept_name, emp_name, salary) VALUES
('技术部', '张三', 15000.00),
('技术部', '李四', 18000.00),
('技术部', '王五', 18000.00),
('技术部', '赵六', 12000.00),
('技术部', '孙七', 20000.00),
('产品部', '周八', 16000.00),
('产品部', '吴九', 14000.00),
('产品部', '郑十', 17000.00),
('产品部', '冯十一', 15000.00),
('运营部', '陈十二', 13000.00),
('运营部', '褚十三', 11000.00),
('运营部', '卫十四', 14000.00);
二、ROW_NUMBER窗口函数基础用法
ROW_NUMBER是SQL中常用的窗口函数,作用是对查询结果集的每一行分配一个唯一的连续整数,整数的值取决于ORDER BY子句指定的排序规则,基本语法如下:
ROW_NUMBER() OVER (
PARTITION BY 分组字段
ORDER BY 排序字段 [ASC|DESC]
)
其中PARTITION BY用于指定分组的字段,这里我们需要按部门分组,所以填写dept_name;ORDER BY用于指定每个分组内的排序规则,查询薪资前三名需要按薪资降序排列,所以填写salary DESC。
三、完整实现各部门薪资前三名查询
结合ROW_NUMBER函数和子查询,我们可以快速得到各部门薪资前三名的完整结果,具体SQL如下:
-- 查询各部门薪资前三名
SELECT
dept_name,
emp_name,
salary
FROM (
-- 子查询:给每个部门的员工按薪资降序编号
SELECT
dept_name,
emp_name,
salary,
ROW_NUMBER() OVER (
PARTITION BY dept_name
ORDER BY salary DESC
) AS salary_rank
FROM employee_salary
) tmp
-- 筛选排名前三的记录
WHERE salary_rank <= 3;
上述SQL的执行逻辑是先通过子查询为每个部门的员工按薪资从高到低生成序号,序号为1的是该部门薪资最高的员工,序号为2的是第二高,以此类推,最后外层查询筛选出序号小于等于3的记录,就得到了各部门薪资前三名。
四、不同窗口函数的区别说明
如果是薪资有并列的情况,ROW_NUMBER会给并列的薪资分配不同的序号,比如两个员工薪资都是18000,一个排第2,一个排第3,如果你需要并列薪资占同一个名次,可以换成DENSE_RANK函数,语法和ROW_NUMBER类似:
-- 使用DENSE_RANK实现并列薪资同排名的前三名查询
SELECT
dept_name,
emp_name,
salary
FROM (
SELECT
dept_name,
emp_name,
salary,
DENSE_RANK() OVER (
PARTITION BY dept_name
ORDER BY salary DESC
) AS salary_rank
FROM employee_salary
) tmp
WHERE salary_rank <= 3;
两者的核心区别如下:
ROW_NUMBER:不管薪资是否相同,都会生成连续不重复的唯一序号,适合需要明确排名顺序、不允许多个记录占同一个名次的场景DENSE_RANK:相同薪资会分配相同的序号,下一个不同薪资的序号不会跳过,比如两个第2名之后,下一个是3名,适合允许并列排名的场景RANK:相同薪资分配相同序号,但是下一个不同薪资的序号会跳过占用的名次,比如两个第2名之后,下一个是4名
五、实际业务注意事项
在实际使用这个查询逻辑时,需要注意以下几点:
- 如果薪资字段存在NULL值,ORDER BY DESC时NULL值会排在最前面,需要根据业务需求处理NULL值,比如用
COALESCE(salary, 0)将NULL转为0再排序 - 如果部门名称存在重复或者大小写不一致的情况,需要先做数据清洗,保证PARTITION BY分组的准确性
- 如果数据量较大,建议在dept_name和salary字段上建立联合索引,提升窗口函数的执行效率
SQLROW_NUMBER窗口函数薪资查询修改时间:2026-07-15 20:24:35