SQL关键字三分钟入门:ROW_NUMBER() —— 窗口函数为每一行编号
在进行数据分析时,我们常常需要为查询结果集中的每条记录生成一个唯一的序号或行号。例如:
- 为每位员工按照入职时间排序并编号;
- 按照订单金额对订单进行排序,并给每个订单分配一个顺序编号;
- 在分组数据内为每条记录编号。
这时候就需要使用 SQL 中非常有用的窗口函数 —— ROW_NUMBER()
!
它可以帮助我们轻松地为查询结果集中的每一行分配一个独一无二的行号,特别适合处理涉及排序和分组的数据分析任务。
什么是 ROW_NUMBER()?
ROW_NUMBER()
是 SQL 中用于为分区或整个结果集中的行生成唯一行号的窗口函数。它会根据指定的排序规则为每条记录分配一个连续的行号,即使存在相同的值也不会导致行号跳过。
你可以把它理解为:“按照某个标准给每条记录打上独一无二的行号标签”。
基本语法
SELECT column1, column2, ...,
ROW_NUMBER() OVER ([PARTITION BY partition_column]ORDER BY sort_column [ASC|DESC], ...
) AS row_num
FROM table_name;
PARTITION BY
(可选):定义如何将数据划分为不同的分区(类似于GROUP BY
),每个分区独立进行编号。ORDER BY
:指定用于决定行号顺序的列及其方向(升序或降序)。
示例讲解
假设我们有一个 employees
表,记录了员工的信息:
employee_id | name | hire_date |
---|---|---|
1 | 张三 | 2020-06-01 |
2 | 李四 | 2019-05-23 |
3 | 王五 | 2021-07-15 |
4 | 赵六 | 2020-08-10 |
5 | 孙七 | 2018-03-12 |
示例1:按入职日期为所有员工编号
SELECT name, hire_date,
ROW_NUMBER() OVER (ORDER BY hire_date ASC) AS row_num
FROM employees;
结果:
name | hire_date | row_num |
---|---|---|
孙七 | 2018-03-12 | 1 |
李四 | 2019-05-23 | 2 |
张三 | 2020-06-01 | 3 |
赵六 | 2020-08-10 | 4 |
王五 | 2021-07-15 | 5 |
注意:这里我们按入职日期进行了升序排列,并为每位员工分配了一个连续的行号。
示例2:按部门对员工进行编号(使用 PARTITION BY)
假设我们增加了一个 department
列来表示员工所属的部门:
employee_id | name | department | hire_date |
---|---|---|---|
1 | 张三 | 销售部 | 2020-06-01 |
2 | 李四 | 销售部 | 2019-05-23 |
3 | 王五 | 技术部 | 2021-07-15 |
4 | 赵六 | 技术部 | 2020-08-10 |
5 | 孙七 | 销售部 | 2018-03-12 |
SELECT name, department, hire_date,
ROW_NUMBER() OVER (PARTITION BY department ORDER BY hire_date ASC) AS row_num
FROM employees;
结果:
name | department | hire_date | row_num |
---|---|---|---|
孙七 | 销售部 | 2018-03-12 | 1 |
李四 | 销售部 | 2019-05-23 | 2 |
张三 | 销售部 | 2020-06-01 | 3 |
王五 | 技术部 | 2021-07-15 | 1 |
赵六 | 技术部 | 2020-08-10 | 2 |
这里我们按部门进行了划分,然后在每个部门内部根据入职日期进行编号。
示例3:结合其他聚合函数使用
有时候你可能还需要同时计算一些汇总信息,比如总人数、平均入职年限等。下面的例子展示了如何同时显示编号和部门总人数:
SELECT name, department, hire_date,
ROW_NUMBER() OVER (PARTITION BY department ORDER BY hire_date ASC) AS row_num,
COUNT(*) OVER (PARTITION BY department) AS total_employees_in_dept
FROM employees;
结果:
name | department | hire_date | row_num | total_employees_in_dept |
---|---|---|---|---|
孙七 | 销售部 | 2018-03-12 | 1 | 3 |
李四 | 销售部 | 2019-05-23 | 2 | 3 |
张三 | 销售部 | 2020-06-01 | 3 | 3 |
王五 | 技术部 | 2021-07-15 | 1 | 2 |
赵六 | 技术部 | 2020-08-10 | 2 | 2 |
注意
对比项 | 使用建议 |
---|---|
唯一行号 | 即使有相同的排序值,ROW_NUMBER() 也会为每条记录分配一个唯一的行号 |
不同的排名函数 | - RANK() :允许并列排名,但会导致行号跳跃 |
- DENSE_RANK() :与 RANK() 类似,但不会跳过行号 | |
性能优化 | 尽量减少 PARTITION BY 的范围以提高性能 |
数据一致性 | 在执行复杂的编号操作时,确保数据的一致性和准确性 |
总结对比表
场景 | SQL 示例 |
---|---|
按单一条件编号 | SELECT ..., ROW_NUMBER() OVER (ORDER BY hire_date ASC) AS row_num FROM employees; |
按部门内条件编号 | SELECT ..., ROW_NUMBER() OVER (PARTITION BY department ORDER BY hire_date ASC) AS row_num FROM employees; |
结合其他聚合函数使用 | SELECT ..., ROW_NUMBER() OVER (PARTITION BY department ORDER BY hire_date ASC) AS row_num, COUNT(*) OVER (PARTITION BY department) AS total_employees_in_dept FROM employees; |
希望这篇简短的指南帮助你快速了解了 ROW_NUMBER()
窗口函数的强大功能。