SQL使用OFFSET/FETCH NEXT实现分页对比ROW_NUMBER,性能差异

本文详细介绍使用CTE递归循环进行大规模数据插入的方法,并对比分析了row_number与fetchnext在不同数据量下的查询性能,揭示了fetchnext在大数量级数据查询中的优势。

第一步,创建表

  • CREATE TABLE Customers (  
  •     CustomerID INT,  
  •     CustomerNumber CHAR(4),  
  •     CustomerName VARCHAR(50),  
  •     CustomerCity VARCHAR(20) )  
  • GO  

第二步,编写语句

插入语句

/*使用CTE递归循环插入 运用CTE递归插入,速度较快*/  
TRUNCATE table Customers  
GO 
DBCC DROPCLEANBUFFERS  
DBCC FREEPROCCACHE  
  
SET STATISTICS IO ON;  
SET STATISTICS TIME ON;  
GO  
  
DECLARE @d Datetime  
SET @d=getdate();  
  
WITH Seq (num,CustomerNumber, CustomerName, CustomerCity) AS  
(SELECT 1,'0000','Customer 0',cast('X-City' as NVARCHAR(20))  
UNION ALL  
SELECT num + 1,'0000','Customer 0',  
cast(CHAR(65 + (num % 26)) + '-City' AS NVARCHAR(20))  
FROM Seq  
WHERE num <=   10000/*CTE批量递归500万数据量 用时一般大概3分钟 因个人电脑而已5000000*/
)  
INSERT INTO Customers (CustomerNumber, CustomerName, CustomerCity)  
SELECT CustomerNumber, CustomerName, CustomerCity  
FROM Seq  
OPTION (MAXRECURSION 0)  
  
select [500万数据量插入完毕,共花费时间(毫秒)]=datediff(ms,@d,getdate())  
  
SET STATISTICS IO OFF ;  
SET STATISTICS TIME OFF;  
GO

查询语句row_number

SET STATISTICS IO ON;
SET STATISTICS TIME ON;

SELECT * FROM
(
    SELECT ROW_NUMBER() OVER (ORDER BY customerid desc) AS 'RowNumber', * FROM Customers
) AS UserInfo
WHERE UserInfo.customerid BETWEEN 5001 AND 5020

SET STATISTICS IO OFF;
SET STATISTICS TIME OFF;

查询语句fetchnext

SET STATISTICS IO ON;
SET STATISTICS TIME ON;
select * from Customers order by customerid desc offset 5000 rows fetch next 20 rows only
SET STATISTICS IO OFF;
SET STATISTICS TIME OFF;

第三步,查询对比

1万条数据量查询对比

逻辑读取次数和执行时间fetchnext 获得一分

另外分别测试了读取开头100条,和末尾20条的情况。末尾20条查询亮着性能详尽,fetchnext还是相对快一点。

50万条数据量查询对比

逻辑读取次数和执行时间fetchnext 获得一分

另外分别测试了读取开头100条,和末尾20条的情况。末尾20条查询亮着性能详尽,fetchnext还是相对快一点。

500万条数据量查询对比

逻辑读取次数和执行时间fetchnext 获得一分,fetchnext 逻辑读取和执行时间性能是rownumber的一倍

1亿条数据量查询对比(final battle)  

再多不测了,哇咔咔。

下面是废话,哈哈哈,人生么,务实也要务虚。

500万数据插入用了3分钟。

5000万数据,推测30分钟。

1亿额,难道用1个小时么刚刚过去了三分钟,还在查询,那就让子弹再飞一会。

 

刚刚去看了看风景,思考了下人生。回来一看,额,还有个sql在执行,还没完,数据量确实有点大了。我还是暂停了语句吧

打算顺便测试下 count(1) 和(*)到底有啥区别,目前来看,500万以下的数据量查询效果一样,没啥区别

5千万条数据量查询对比(final battle)

吃完午饭眯会,继续码字,1亿条太多了,电脑卡死

 

逻辑读取次数和执行时间fetchnext 获得一分,此时已经是10倍的效率差距了

同时验证了下count(*)和count(1)的区别

 

对比结果

fetch性能高

简单数据下:count(1)和count(*)性能么有什么大的差别。推测,复杂数据表1比*性能高

 

<think>我们正在讨论SQL Server中的ROW_NUMBER()函数。根据引用内容,ROW_NUMBER()函数用于为结果集中的每一行分配一个唯一的顺序号,特别适用于分页查询。 引用[2]和引用[3]详细说明了基本语法:ROW_NUMBER() OVER (PARTITION BY partition_expression ORDER BY order_expression) AS row_number 其中: - PARTITION BY 子句可选,用于将结果集划分为多个分区,函数在每个分区内单独排序编号。 - ORDER BY 子句必需,用于指定排序规则,行号将根据此排序生成。 下面是一个简单的示例,展示如何使用ROW_NUMBER()函数进行分页查询(参考引用[1]和引用[2]): 假设我们有一个表your_table,包含column1, column2等列,我们想按column1排序并分页,每页显示一定数量的行。 示例代码: ```sql -- 定义分页参数(例如:第3页,每页10行,则起始行为21,结束行为30) DECLARE @PageNumber INT = 3; DECLARE @PageSize INT = 10; DECLARE @startRowIndex INT = (@PageNumber - 1) * @PageSize + 1; DECLARE @endRowIndex INT = @PageNumber * @PageSize; -- 使用ROW_NUMBER()进行分页查询 SELECT * FROM ( SELECT column1, column2, ROW_NUMBER() OVER (ORDER BY column1) AS RowNumber FROM your_table ) AS SubQuery WHERE RowNumber BETWEEN @startRowIndex AND @endRowIndex; ``` 此外,ROW_NUMBER()函数还可以配合PARTITION BY使用,用于在每个分区内进行编号(如引用[3]和引用[5]所示)。例如,按部门(dept_id)分区,每个部门内按站点(stcd)排序: ```sql SELECT dept_id, stcd, ROW_NUMBER() OVER (PARTITION BY dept_id ORDER BY STCD) AS 排序 FROM tb_station_config; ``` 引用[4]提到,ROW_NUMBER()还可以用于数据去重,例如在Hive中,但同样适用于SQL Server。例如,删除重复数据(保留每组重复数据中的第一条): ```sql -- 假设我们有一个表,有重复数据,我们想根据某些列去重(例如按col1, col2分组,按时间列ts排序,保留最新的一条) WITH CTE AS ( SELECT *, ROW_NUMBER() OVER (PARTITION BY col1, col2 ORDER BY ts DESC) AS rn FROM your_table ) DELETE FROM CTE WHERE rn > 1; ``` 总结ROW_NUMBER()函数的用法: 1. 分页查询:通过为整个结果集排序编号,然后选取指定行号范围。 2. 分区排序:在分组(PARTITION BY)内按指定顺序排序并编号。 3. 数据去重:通过分区排序,删除重复数据(保留每组的第一条或最后一条)。 注意:ROW_NUMBER()函数分配的行号是唯一的(即使排序字段相同,行号也不会重复,它会根据OVER子句中ORDER BY的顺序任意分配不同的行号)。如果希望相同值有相同行号,可以考虑使用RANK()或DENSE_RANK()函数。 根据以上内容,我们可以生成相关问题。</think>在 SQL Server 中,`ROW_NUMBER()` 函数用于为结果集的每一行分配唯一的顺序号,特别适用于分页查询、数据分区和去重操作。以下是详细用法: ### 基本语法 ```sql ROW_NUMBER() OVER ( [PARTITION BY partition_expression] ORDER BY sort_expression [ASC|DESC] ) AS row_number ``` - **PARTITION BY**(可选):将结果集分组,函数在每个分组内单独编号 - **ORDER BY**(必需):定义排序规则,编号基于此顺序生成 - **AS row_number**:生成的行号列别名 --- ### 典型应用场景 #### 1. 分页查询(最常用) ```sql -- 获取第11-20条记录(每页10条,第2页) SELECT * FROM ( SELECT *, ROW_NUMBER() OVER (ORDER BY CreateDate DESC) AS RowNum FROM Orders ) AS SubQuery WHERE RowNum BETWEEN 11 AND 20 ``` #### 2. 分区内排序(按部门分组编号) ```sql SELECT EmployeeID, DeptID, Salary, ROW_NUMBER() OVER ( PARTITION BY DeptID ORDER BY Salary DESC ) AS DeptRank FROM Employees ``` 结果示例: | EmployeeID | DeptID | Salary | DeptRank | |------------|--------|--------|----------| | 101 | 1 | 9000 | 1 | | 102 | 1 | 8000 | 2 | | 203 | 2 | 8500 | 1 | [^5] #### 3. 数据去重(保留每组最新记录) ```sql WITH CTE AS ( SELECT *, ROW_NUMBER() OVER ( PARTITION BY UserID ORDER BY LoginTime DESC ) AS rn FROM LoginRecords ) DELETE FROM CTE WHERE rn > 1 ``` > 此操作会删除同一用户的重复登录记录,仅保留最近一次[^4] --- ### 注意事项 1. **排序字段必选**:`ORDER BY` 是强制子句,否则报错 2. **行号唯一性**:即使排序字段值相同,也会分配不同行号(区别于 `RANK()`) 3. **性能优化**:对排序列建立索引可显著提升大表分页效率 4. **与 TOP 结合**:可嵌套使用实现高效随机抽样 ```sql SELECT TOP 5 * FROM ( SELECT *, ROW_NUMBER() OVER (ORDER BY NEWID()) AS rn FROM Products ) t ``` > ⚠️ 在 SQL Server 2012+ 版本中,推荐使用 `OFFSET-FETCH` 替代分页: > ```sql > SELECT * FROM Orders > ORDER BY OrderID > OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY > ``` [^2] --- ### 常见错误解决方案 **问题:** 分页时出现重复数据 **原因:** 排序字段不唯一导致行号分配不稳定 **修复:** 添加第二排序字段确保唯一性 ```sql ROW_NUMBER() OVER ( ORDER BY CreateDate, OrderID -- 添加唯一字段 ) AS RowNum ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值