MySQL 中 ROW_NUMBER() 函数最佳实践

 更新时间:2025年06月26日 15:09:06   作者:比特森林探险记  
MySQL中ROW_NUMBER()函数,作为窗口函数为每行分配唯一连续序号,区别于RANK()和DENSE_RANK(),特别适合分页、去重、TopN等需要精确顺序控制的场景,本文给大家介绍MySQL中ROW_NUMBER()函数,感兴趣的朋友一起看看吧

MySQL 中 ROW_NUMBER() 函数详解

ROW_NUMBER() 是 SQL 窗口函数中的一种,用于为查询结果集中的每一行分配一个​​唯一的连续序号​​。与 RANK() 和 DENSE_RANK() 不同,ROW_NUMBER() 不会处理重复值,即使排序字段值相同,也会严格按行顺序递增编号。

一、基础语法

ROW_NUMBER() OVER (
    [PARTITION BY 分组字段]
    ORDER BY 排序字段 [ASC|DESC]
)
  • ​PARTITION BY​​:按指定字段分组,每组内重新从1开始编号。
  • ​ORDER BY​​:决定排序逻辑,影响行号的分配顺序。

二、核心特点

​特性​​说明​
唯一性每行序号严格递增,不重复(即使排序字段值相同)
灵活性可结合分组(PARTITION BY)实现复杂场景
兼容性MySQL 8.0+ 原生支持,低版本需用变量模拟
性能影响未优化时可能导致全表扫描,需合理使用索引

三、典型应用场景

1. 数据分页查询

-- 查询第3页数据(每页10条)
WITH paged_data AS (
    SELECT 
        id, name, 
        ROW_NUMBER() OVER (ORDER BY id) AS row_num
    FROM users
)
SELECT * 
FROM paged_data 
WHERE row_num BETWEEN 21 AND 30;

2. 删除重复数据

-- 保留最新记录(假设 create_time 为时间戳)
DELETE FROM orders
WHERE (id, product_id) IN (
    SELECT id, product_id FROM (
        SELECT 
            id, product_id,
            ROW_NUMBER() OVER (
                PARTITION BY product_id 
                ORDER BY create_time DESC
            ) AS rn
        FROM orders
    ) t 
    WHERE rn > 1  -- 删除重复项,保留最新一条
);

3. 分组取Top N记录

-- 获取每个部门薪资前3名
SELECT *
FROM (
    SELECT 
        name, department, salary,
        ROW_NUMBER() OVER (
            PARTITION BY department 
            ORDER BY salary DESC
        ) AS dept_rank
    FROM employees
) ranked
WHERE dept_rank <= 3;

4. 生成唯一流水号

-- 按日期生成订单流水号(格式:YYYYMMDD-0001)
SELECT 
    order_id,
    CONCAT(
        DATE_FORMAT(create_time, '%Y%m%d'), 
        '-', 
        LPAD(ROW_NUMBER() OVER (
            PARTITION BY DATE(create_time) 
            ORDER BY create_time
        ), 4, '0')
    ) AS serial_num
FROM orders;

四、与其他排序函数对比

函数重复值处理示例结果(排序字段值相同)
ROW_NUMBER()强制分配不同序号1, 2, 3, 4
RANK()相同值共享排名,后续跳过序号1, 1, 3, 4
DENSE_RANK()相同值共享排名,后续连续递增1, 1, 2, 3
-- 对比三种函数
SELECT 
    score,
    ROW_NUMBER() OVER (ORDER BY score DESC) AS row_num,
    RANK() OVER (ORDER BY score DESC) AS rank,
    DENSE_RANK() OVER (ORDER BY score DESC) AS dense_rank
FROM exam_scores;

五、性能优化技巧

1. 索引设计

为 PARTITION BY 和 ORDER BY 涉及的字段创建联合索引:

CREATE INDEX idx_dept_salary ON employees(department, salary DESC);

2. 减少计算范围

-- 仅处理2023年数据
SELECT *
FROM (
    SELECT 
        order_id, amount,
        ROW_NUMBER() OVER (ORDER BY amount DESC) AS rn
    FROM orders
    WHERE YEAR(order_date) = 2023  -- 先过滤再排序
) t
WHERE rn <= 100;

3. 避免嵌套查询

-- 优化前(性能差)
SELECT * FROM (
    SELECT *, ROW_NUMBER() OVER (...) AS rn
    FROM large_table
) t WHERE rn <= 100;
-- 优化后(直接使用LIMIT,若逻辑允许)
SELECT *, ROW_NUMBER() OVER (...) AS rn
FROM large_table
ORDER BY ...
LIMIT 100;

六、MySQL低版本兼容方案(5.7及以下)

使用会话变量模拟 ROW_NUMBER()

-- 按部门分组排序
SELECT 
    department, name, salary,
    @row_num := IF(
        @current_dept = department, 
        @row_num + 1, 
        1
    ) AS row_num,
    @current_dept := department AS dummy
FROM employees
ORDER BY department, salary DESC;

七、常见错误与排查

1. 错误:序号不符合预期

  • ​原因​​:未正确指定 ORDER BY 或 PARTITION BY
  • ​解决​​:检查排序字段是否明确,分组条件是否合理

2. 错误:性能低下

  • ​原因​​:未使用索引导致全表扫描
  • ​解决​​:使用 EXPLAIN 分析执行计划,添加必要索引

3. 错误:结果集为空

  • ​原因​​:外层查询条件与子查询中的 WHERE 冲突
  • ​解决​​:验证过滤条件逻辑

八、最佳实践

  • ​明确排序规则​​:始终显式指定 ORDER BY 的排序方向(ASC/DESC)
  • ​慎用全局排序​​:避免无 PARTITION BY 的大数据集操作
  • ​监控内存使用​​:窗口函数可能消耗大量临时内存
  • ​版本验证​​:生产环境确认 MySQL 版本 >= 8.0
  • ​结合 CTE 使用​​:提高复杂查询的可读性
WITH ranked_products AS (
    SELECT 
        product_id,
        ROW_NUMBER() OVER (PARTITION BY category ORDER BY sales DESC) AS rn
    FROM products
)
SELECT * FROM ranked_products WHERE rn = 1;

​总结​​:ROW_NUMBER() 是处理行级序号分配的利器,特别适合需要精确控制行顺序的场景。合理使用可显著简化分页、去重、Top N查询等操作,但需注意其对性能的影响,尤其在处理海量数据时需结合索引优化。

到此这篇关于MySQL 中 ROW_NUMBER() 函数详解的文章就介绍到这了,更多相关mysql row_number()函数内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • MySQL死锁日志的实例分析技巧总结

    MySQL死锁日志的实例分析技巧总结

    MySQL死锁是线上经常遇到的现象,MySQL死锁日志分析方法有助于研发快速提取信息,提高分析效率,通过了解死锁触发条件、检测机制及锁类型,结合日志分析工具,可以更有效地解决死锁问题
    2024-11-11
  • window10中mysql8.0修改端口port不生效的解决方法

    window10中mysql8.0修改端口port不生效的解决方法

    mysql配置文件默认位置,端口号等信息需要在my.ini文件中修改,若修改安装位置的my-default文件文件或新建my.ini文件是不生效的,本文主要介绍了window10中mysql8.0修改端口port不生效的解决方法,感兴趣的可以了解一下
    2023-11-11
  • 优化mysql数据库的经验总结

    优化mysql数据库的经验总结

    本篇文章是对优化mysql数据库的经验进行了详细的总结介绍,需要的朋友参考下
    2013-06-06
  • MySQL自增ID用完了的四种解决方式

    MySQL自增ID用完了的四种解决方式

    这篇文章主要介绍了MySQL自增ID用完了的四种解决方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-06-06
  • mysql中insert与select的嵌套使用方法

    mysql中insert与select的嵌套使用方法

    这篇文章主要介绍了mysql中insert与select的嵌套使用方法,代码功能非常实用,需要的朋友可以参考下
    2014-07-07
  • SQL实战之行列互转

    SQL实战之行列互转

    本文介绍了在Hive中进行行转列的几种方法,包括使用CASE WHEN/IF、Get_Json_Object、Str_To_Map以及UNION ALL和EXPLODE函数,每种方法都有其适用场景,感兴趣的可以了解一下
    2024-12-12
  • Mysql大表全表update的的实现

    Mysql大表全表update的的实现

    有些时候在进行一些业务迭代时需要我们对Mysql表中数据进行全表update,本文主要介绍了Mysql大表update的的实现
    2024-08-08
  • MySQL使用UUID_SHORT()的问题解决

    MySQL使用UUID_SHORT()的问题解决

    MySQL的UUID_SHORT()函数是一个用于生成短UUID的函数,该函数返回一个64位的整数,可以用于唯一标识一条数据记录,本文介绍了MySQL使用UUID_SHORT()的问题解决,感兴趣的可以了解一下
    2023-08-08
  • Mysql关于数据库是否应该使用外键约束详解说明

    Mysql关于数据库是否应该使用外键约束详解说明

    MySQL 外键约束(FOREIGN KEY)是表的一个特殊字段,经常与主键约束一起使用。对于两个具有关联关系的表而言,相关联字段中主键所在的表就是主表,外键所在的表就是从表。外键用来建立主表与从表的关联关系,为两个表的数据建立连接,约束两个表中数据的一致性和完整性
    2021-10-10
  • 详解MySQL中事务隔离级别的实现原理

    详解MySQL中事务隔离级别的实现原理

    这篇文章主要介绍了MySQL中事务隔离级别的实现原理,帮助大家更好的理解和使用MySQL数据库,感兴趣的朋友可以了解下
    2021-01-01

最新评论