如何高效编写SQL获取表中第N高主键ID?两种写法哪种更优
第N高Customer_Id查询方案性能分析与优化
现有两种方案性能对比
你提供的两种查询语句如下:
-- 方案A select top 1 Customer_Id from customers c where c.Customer_Id not in( select top 2 cc.Customer_Id from customers cc order by cc.Customer_Id desc ) order by c.Customer_Id desc
-- 方案B select top 1 temp.Customer_Id from ( select top 3 c.Customer_Id from customers c order by c.Customer_Id desc) as temp order by temp.Customer_Id
- 性能上方案B远优于方案A:
方案A使用NOT IN子查询实现排除前2高的ID,需要额外做一次主键值的匹配校验,当表数据量较大时,这部分匹配开销会明显上升。
方案B仅做了两次有序的TOP N扫描,因为Customer_Id是主键,自带有序B+树索引,两次扫描都可以直接走索引,不需要额外匹配操作,开销远低于方案A。
更通用的高性能实现方案
针对适配任意第N高ID的需求,推荐使用以下两种兼容性更强、性能更优的写法:
1. OFFSET FETCH 写法(主流数据库通用,最简洁)
SQL Server 2012+、PostgreSQL、MySQL 8.0+等主流关系型数据库都支持该语法,只需要修改OFFSET后的数值即可调整取第几高的ID,第N高对应OFFSET为N-1:
-- 取第3高ID写法,要取第N高就把OFFSET后的2改为N-1 SELECT Customer_Id FROM customers ORDER BY Customer_Id DESC OFFSET 2 ROWS FETCH NEXT 1 ROWS ONLY;
该写法可以让数据库优化器直接定位到主键索引降序的第N行,不需要额外的子查询或排序操作,性能最优。
2. 窗口函数写法(支持复杂排名规则)
如果需求涉及并列排名(比如两个相同ID算同一名次),可以使用窗口函数实现,灵活性更高:
-- 无并列排名需求用ROW_NUMBER,有并列同排名需求用DENSE_RANK WITH ranked_customers AS ( SELECT Customer_Id, ROW_NUMBER() OVER(ORDER BY Customer_Id DESC) AS rank_num FROM customers ) -- 取第3高就把rank_num = 3的3改为对应N值 SELECT Customer_Id FROM ranked_customers WHERE rank_num = 3;
该写法同样可以走主键索引,仅需一次全表索引扫描即可完成排名,性能也远高于嵌套子查询写法。
内容的提问来源于stack exchange,提问作者חיים חדד
相关产品推荐
相关产品推荐

