当前位置: 代码网 > it编程>数据库>Mysql > MySQL 中 ROW_NUMBER() 函数最佳实践

MySQL 中 ROW_NUMBER() 函数最佳实践

2025年06月26日 Mysql 我要评论
mysql 中row_number()函数详解row_number()是 sql 窗口函数中的一种,用于为查询结果集中的每一行分配一个​​唯一的连续序号​​。与rank()和dense_rank()不

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()函数内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com