sql 分页(原:http://www.cnblogs.com/fly_zj/archive/2010/07/06/1772536.html)
2015-07-13 09:35
555 查看
SQLSERVER2005分页脚本性能实测
网上有很多的分页T-SQL代码,分散在各处,主要的问题是:测试时数据量太小,最多只有2万多条,不同方法的体现出性能差别有疑惑,另外当初在学习sqlserver2005时,一位同学信誓旦旦说分页在SQLSERVER2005中可以使用EXCEPT关键字,性能最好,理由是EXCEPT是集合运算。当时信以为真。工作以后,发现在SQLSERVER2005中的分页存储过程都没有用到EXCEPT方法,就更疑惑了。
这次系统的看《
--在我电脑上该数据库的创建持续1分钟多SETNOCOUNT ON; USEmaster; GO IFDB_ID('Performance') IS NOT NULL DROP DATABASEPerformance; ELSE CREATE DATABASEPerformance; GO USEPerformance; GO --创建辅助表Nums,1百万行数据 IFOBJECT_ID('dbo.Nums') IS NOT NULL DROP TABLEdbo.Nums; GO CREATE TABLEdbo.Nums(n INT NOT NULL PRIMARY KEY); DECLARE@ max AS INT,@rc AS INT; SET@ max=1000000; SET@rc=1; INSERT INTONums VALUES(1); WHILE@rc*2<=@ max BEGIN INSERT INTOdbo.Nums SELECTn+@rc FROMdbo.Nums; SET@rc=@rc*2; END INSERT INTOdbo.Nums SELECTn+@rc FROMdbo.Nums WHEREn+@rc<=@ max; GO --如果存在dbo.Orders表则删除 IFOBJECT_ID('dbo.Orders') IS NOT NULL DROP TABLEdbo.Orders; GO --定义写变量,以此来创建随机的数据,不明白就忽略算了
DECLARE @numorders AS INT, @numcusts AS INT, @numemps AS INT, @numshippers AS INT, @numyears AS INT, @startdate AS DATETIME; SELECT @numorders=1000000, @numcusts=20000, @numemps=500, @numshippers=5, @numyears=4, @startdate='20030101'; --创建Orders表 CREATE TABLEdbo.Orders ( orderid INT NOT NULL, custid CHAR(11) NOT NULL, empid INT NOT NULL, shipperid VARCHAR(5) NOT NULL, orderdate DATETIME NOT NULL, filler CHAR(155) NOT NULL DEFAULT('a') ); --随机的填入一些数据,1百万行数据 INSERT INTOdbo.Orders(orderid,custid,empid,shipperid,orderdate) SELECTn ASorderid, 'C'+ RIGHT('000000000' + CAST( 1+ABS(CHECKSUM(NEWID()))%@numcusts AS VARCHAR(10)),10) AScustid, 1+ABS(CHECKSUM(NEWID()))%@numemps ASempid, CHAR(ASCII('A')-2 +2*(1+ABS(CHECKSUM(NEWID()))%@numshippers)) ASshipperid, DATEADD( day,n/(@numorders/(@numyears*365.25)),@startdate) asorderdate FROMdbo.Nums WHEREn<=@numorders ORDER BYCHECKSUM(NEWID());
--为orderid创建主键,为custid,empid添加索引,并包含shipperid,orderdate列ALTER TABLEdbo.Orders ADD CONSTRAINTPK_Orders_orderid PRIMARY KEY CLUSTERED(orderid); CREATE INDEXidx_Orders_custid_empid ONdbo.Orders(custid,empid);
第一种分页方法:使用TOP与NOTIN来分页。注意,获取T-SQL脚本运行的时间,单击SSMS工具栏上的【包含客户端统计信息】按钮。
--把SQLSERVER执行计划缓存清空DBCCFREEPROCCACHE; DBCCFREESYSTEMCACHE('ALL');
脚本如下:
CREATE PROCEDUREusp_EvaluatePerformanceBy_Top_In
@pagesizeINT,
@pagenumINT AS SELECT TOP(@pagesize)o1.orderid,o1.custid,o1.empid FROMdbo.Orderso1 WHEREo1.orderid NOT IN( SELECT TOP((@pagenum-1)*@pagesize)o2.orderid FROMdbo.Orderso2
);
GO
--当读取1万条附近的20条数据时花的时间为EXECusp_EvaluatePerformanceBy_Top_In@pagesize=20,@pagenum=5000
时间为257.400毫秒
--当读取第20万条附近的20条数据时花的时间为EXECusp_EvaluatePerformanceBy_Top_In@pagesize=20,@pagenum=10000
时间为:152.700,sqlserver利用了缓存的可执行计划,故时间要少
--当读取第80万条附近的20条数据时花的时间为EXECusp_EvaluatePerformanceBy_Top_In@pagesize=20,@pagenum=40000
所花的时间为:240.200,同样sqlserver利用了缓存的可执行计划,时间变化不大
总结一下,当利用top和notin来分页,且要查找的列都已在索引中时,
1万条数据附近,为257.400ms
20万数据条附近,为152.700ms
80万条数据附近,为240.200ms
第二种分页方法使用CTE和Row_Number函数,请看如下的T-SQL代码
--清空可执行计划缓存DBCCFREEPROCCACHE; DBCCFREESYSTEMCACHE('ALL');
--使用CTE和ROW_NUMBER()来分页CREATE PROCEDUREusp_EvaluatePerformanceBy_Row_Number
@pagesizeINT,
@pagenumINT AS WITHTmp AS
(SELECT ROW_NUMBER() OVER( ORDER BYorderid ASC) AScolnum,
orderid,
custid,
empidFROMdbo.Orderso1
)SELECTorderid,custid,empid FROMTmp WHEREcolnum>(@pagenum-1)*@pagesize ANDcolnum<=@pagenum*@pagesize; GO
--当读取1万条附近的20条数据时花的时间为EXECusp_EvaluatePerformanceBy_Row_Number@pagesize=20,@pagenum=500
所花费的时间为:21.500
--当读取第20万条附近的20条数据时花的时间为EXECusp_EvaluatePerformanceBy_Row_Number@pagesize=20,@pagenum=10000
所花费的时间为:44.900
--当读取第80万条附近的20条数据时花的时间为EXECusp_EvaluatePerformanceBy_Row_Number@pagesize=20,@pagenum=40000
所花费的时间为:118.400
总结一下,当利用CTE和ROW_NUMBER来分页,且要查找的列都在索引中时,
1万条数据附近,为21.500ms
20万数据条附近,为44.900ms
80万条数据附近,为118.400ms
第三种分页的方法是使用EXCEPT,请看如下的T-SQL代码
--清空可执行计划缓存DBCCFREEPROCCACHE; DBCCFREESYSTEMCACHE('ALL');
--利用except来求分页CREATE PROCEDUREusp_EvaluatePerformanceBy_Except
@pagesizeINT,
@pagenumINT AS SELECT TOP(@pagesize*@pagenum)orderid,
custid,
empidFROMdbo.Orders EXCEPT( SELECT TOP((@pagenum-1)*@pagesize)
orderid,
custid,
empidFROMdbo.Orders
);GO
--当读取第1万条附近的20条数据时花的时间为EXECusp_EvaluatePerformanceBy_Except@pagesize=20,@pagenum=500
所需的时间为:123.000
--当读取第20万条附近的20条数据时花的时间为EXECusp_EvaluatePerformanceBy_Except@pagesize=20,@pagenum=10000
所花时间为:174.600
--当读取第80万条附近的20条数据时花的时间为EXECusp_EvaluatePerformanceBy_Except @pagesize=20,@pagenum=40000
所花时间为:390.200
总结一下,当利用EXCEPT来分页,且要查找的列都在索引中时,
1万条数据附近,为123.000ms
20万数据条附近,为174.600ms
80万条数据附近,为390.200ms
第四种分页的方法是利用TOP和Max函数结合,请看如下的T-SQL代码
--清空可执行计划缓存DBCCFREEPROCCACHE; DBCCFREESYSTEMCACHE('ALL');
--第总方法通过top和max来求值CREATE PROCEDUREusp_EvaluatePerformanceBy_Top_Max
@pagesizeINT,
@pagenumINT AS SELECT TOP(@pagesize)orderid,custid,empid FROMdbo.Orderso1 WHEREo1.orderid>( SELECT max(d.orderid) asnum FROM
(SELECT top((@pagenum-1)*@pagesize)orderid FROMdbo.orderso2 ORDER BYorderid
)ASd
)GO
--当读取第1万条附近的20条数据时花的时间为EXECusp_EvaluatePerformanceBy_Top_Max@pagesize=20,@pagenum=500
时间为:365.100
--当读取第20万条附近的20条数据时花的时间为EXECusp_EvaluatePerformanceBy_Top_Max@pagesize=20,@pagenum=10000
时间为:319.800
--当读取第80万条附近的20条数据时花的时间为EXECusp_EvaluatePerformanceBy_Top_Max@pagesize=20,@pagenum=40000
所花的时间为:137.000
总结一下,当利用TOP和MAX来分页,且要查找的列都在索引中时,
1万条数据附近,为365.100ms
20万数据条附近,为319.800ms
80万条数据附近,为137.000ms
看一看最后的结论:
TOP与NOTIN | TOP与MAX | ROW_NUMBER() | EXCEPT | |
1万条数据 | 257.400ms | 365.100ms | 21.500ms | 123.000ms |
20万条数据 | 152.700ms | 319.800ms | 44.900ms | 174.600ms |
80万条数据 | 240.200ms | 137.000ms | 118.400ms | 390.200ms |
相关文章推荐
- 网络爬虫(一) 网络爬虫基本原理
- 工作中的为人处世
- Linux服务器上监控网络带宽的18个常用命令
- Linux服务器上监控网络带宽的18个常用命令
- Linux-6.2-网络编程基础
- 使用HttpSessionListener接口监听Session的创建和失效
- 网络编程Socket
- ssl+http
- 使用 libevent 和 libev 提高网络应用性能——I/O模型演进变化史
- 自定义的parse_url逆向函数http_build_url,将数组转为url字符串
- Socket使用TCP/IP如何实现通信
- Windows网络故障排查脚本
- 工业串口和网络软件通讯平台(SuperIO 2.1)更新发布
- 工业串口和网络软件通讯平台(SuperIO 2.1)更新发布
- Unity5.1 新的网络引擎UNET(六) UNET Multiplayer Lobby
- Unity5.1 新的网络引擎UNET(五) UNET Network Messages
- Unity5.1 新的网络引擎UNET(四) UNET Remote Actions
- Unity5.1 新的网络引擎UNET(三) UNET NetworkManager
- Unity5.1 新的网络引擎UNET(一) 概括2
- The import javax.servlet.http.HttpServletRequest cannot be resolved