sql 分页(原:http://www.cnblogs.com/fly_zj/archive/2010/07/06/1772536.html)

SQLSERVER 2005分页脚本性能实测

网上有很多的分页T-SQL代码,分散在各处,主要的问题是:测试时数据量太小,最多只有2万多条,不同方法的体现出性能差别有疑惑,另外当初在学习sqlserver 2005 时,一位同学信誓旦旦说分页

在SQLSERVER 2005中可以使用EXCEPT关键字,性能最好,理由是EXCEPT是集合运算。当时信以为真。工作以后,发现在SQLSERVER 2005中的分页存储过程都没有用到EXCEPT方法,就更疑惑了。

这次系统的看《Inside Microsoft® SQL Server™ 2005 T-SQL Querying 》这本书时,发现有个创建数据库脚本,数据时随机的,把它作为测试数非常不错,脚本如下(稍微做调整):

--在我电脑上该数据库的创建持续1分钟多
SET NOCOUNT ON;
USE master;
GO
IF DB_ID(‘Performance‘) IS NOT NULL
	DROP DATABASE Performance;
ELSE
	CREATE DATABASE Performance;
GO
USE Performance;
GO

--创建辅助表Nums,1百万行数据
IF OBJECT_ID(‘dbo.Nums‘) IS NOT NULL
  DROP TABLE dbo.Nums;
GO
CREATE TABLE dbo.Nums(n INT NOT NULL PRIMARY KEY);
DECLARE @max AS INT, @rc AS INT;
SET @max = 1000000;
SET @rc = 1;

INSERT INTO Nums VALUES(1);
WHILE @rc * 2 <= @max
BEGIN
  INSERT INTO dbo.Nums SELECT n + @rc FROM dbo.Nums;
  SET @rc = @rc * 2;
END

INSERT INTO dbo.Nums
  SELECT n + @rc FROM dbo.Nums WHERE n + @rc <= @max;
GO

-- 如果存在dbo.Orders表则删除
IF OBJECT_ID(‘dbo.Orders‘) IS NOT NULL
  DROP TABLE dbo.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 TABLE dbo.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 INTO dbo.Orders(orderid, custid, empid, shipperid, orderdate)
  SELECT n AS orderid,
    ‘C‘ + RIGHT(‘000000000‘
            + CAST(
                1 + ABS(CHECKSUM(NEWID())) % @numcusts
                AS VARCHAR(10)), 10) AS custid,
    1 + ABS(CHECKSUM(NEWID())) % @numemps AS empid,
    CHAR(ASCII(‘A‘) - 2
           + 2 * (1 + ABS(CHECKSUM(NEWID())) % @numshippers)) AS shipperid,
      DATEADD(day, n / (@numorders / (@numyears * 365.25)), @startdate)
		as orderdate
  FROM dbo.Nums
  WHERE n <= @numorders
  ORDER BY CHECKSUM(NEWID());
--为orderid创建主键,为custid,empid添加索引,并包含shipperid,orderdate列
ALTER TABLE dbo.Orders ADD
  CONSTRAINT PK_Orders_orderid PRIMARY KEY CLUSTERED(orderid);

CREATE INDEX idx_Orders_custid_empid ON dbo.Orders(custid,empid) ;

第一种分页方法:使用TOP与NOT IN来分页。注意,获取T-SQL脚本运行的时间,单击SSMS工具栏上的【包含客户端统计信息】按钮。

  
--把SQLSERVER执行计划缓存清空
DBCC FREEPROCCACHE;
DBCC FREESYSTEMCACHE (‘ALL‘);
 
脚本如下:
CREATE PROCEDURE usp_EvaluatePerformanceBy_Top_In
@pagesize INT,
@pagenum INT
AS

SELECT TOP(@pagesize) o1.orderid,o1.custid,o1.empid
FROM dbo.Orders o1
WHERE o1.orderid NOT IN(
	SELECT TOP((@pagenum-1)*@pagesize) o2.orderid
	FROM dbo.Orders o2
);             

GO

--当读取1万条附近的20条数据时花的时间为
EXEC usp_EvaluatePerformanceBy_Top_In @pagesize=20,@pagenum=5000 									

时间为257.400毫秒

  
--当读取第20万条附近的20条数据时花的时间为

EXEC usp_EvaluatePerformanceBy_Top_In @pagesize=20,@pagenum=10000 										

时间为:152.700,sqlserver 利用了缓存的可执行计划,故时间要少

 
--当读取第80万条附近的20条数据时花的时间为

EXEC usp_EvaluatePerformanceBy_Top_In @pagesize=20,@pagenum=40000 
所花的时间为:240.200,同样sqlserver利用了缓存的可执行计划,时间变化不大
 
 										

总结一下,当利用top和not in 来分页,且要查找的列都已在索引中时,

1万条数据附近,为257.400ms

20万数据条附近,为152.700ms

80万条数据附近,为240.200ms

第二种分页方法使用CTE和Row_Number函数,请看如下的T-SQL代码

--清空可执行计划缓存
DBCC FREEPROCCACHE;
DBCC FREESYSTEMCACHE (‘ALL‘);

--使用CTE和ROW_NUMBER()来分页
CREATE PROCEDURE usp_EvaluatePerformanceBy_Row_Number
@pagesize INT,
@pagenum INT
AS

WITH Tmp AS
(
	SELECT	ROW_NUMBER() OVER (ORDER BY orderid ASC) AS colnum,
		orderid,
		custid,
		empid
	FROM dbo.Orders o1
)
SELECT orderid,custid,empid FROM Tmp
WHERE colnum>(@pagenum-1)*@pagesize
AND colnum<[email protected]*@pagesize;

GO

--当读取1万条附近的20条数据时花的时间为

EXEC usp_EvaluatePerformanceBy_Row_Number @pagesize=20,@pagenum=500

所花费的时间为:21.500

--当读取第20万条附近的20条数据时花的时间为

EXEC usp_EvaluatePerformanceBy_Row_Number @pagesize=20,@pagenum=10000

所花费的时间为:44.900

--当读取第80万条附近的20条数据时花的时间为

EXEC usp_EvaluatePerformanceBy_Row_Number @pagesize=20,@pagenum=40000

所花费的时间为:118.400

总结一下,当利用CTE和ROW_NUMBER 来分页,且要查找的列都在索引中时,

1万条数据附近,为21.500ms

20万数据条附近,为44.900ms

80万条数据附近,为118.400ms

第三种分页的方法是使用EXCEPT,请看如下的T-SQL代码

--清空可执行计划缓存
DBCC FREEPROCCACHE;
DBCC FREESYSTEMCACHE (‘ALL‘);

--利用except来求分页

CREATE PROCEDURE usp_EvaluatePerformanceBy_Except
@pagesize INT,
@pagenum INT
AS
SELECT TOP(@pagesize*@pagenum) orderid,
	custid,
	empid
FROM dbo.Orders
EXCEPT (
SELECT TOP((@pagenum-1)*@pagesize) 
         orderid,
	custid,
	empid
FROM dbo.Orders
);
GO

--当读取第1万条附近的20条数据时花的时间为

EXEC usp_EvaluatePerformanceBy_Except @pagesize=20,@pagenum=500
所需的时间为:123.000
 
 
--当读取第20万条附近的20条数据时花的时间为

EXEC usp_EvaluatePerformanceBy_Except @pagesize=20,@pagenum=10000
所花时间为:174.600
 
 
--当读取第80万条附近的20条数据时花的时间为

EXEC usp_EvaluatePerformanceBy_Except	@pagesize=20,@pagenum=40000
所花时间为:390.200
 
 

总结一下,当利用EXCEPT来分页,且要查找的列都在索引中时,

1万条数据附近,为123.000ms

20万数据条附近,为174.600ms

80万条数据附近,为390.200ms

第四种分页的方法是利用TOP和Max函数结合,请看如下的T-SQL代码

  
--清空可执行计划缓存
DBCC FREEPROCCACHE;
DBCC FREESYSTEMCACHE (‘ALL‘);

--第总方法通过top和max来求值
CREATE PROCEDURE usp_EvaluatePerformanceBy_Top_Max
@pagesize INT,
@pagenum INT
AS

SELECT TOP(@pagesize) orderid,custid,empid
FROM dbo.Orders o1
WHERE o1.orderid>(
	SELECT max(d.orderid) as num FROM
	(
		SELECT top((@pagenum-1)*@pagesize) orderid
		FROM dbo.orders o2 ORDER BY orderid
	)AS d   

)
GO

--当读取第1万条附近的20条数据时花的时间为

EXEC usp_EvaluatePerformanceBy_Top_Max @pagesize=20,@pagenum=500
时间为:365.100
 
 
--当读取第20万条附近的20条数据时花的时间为

EXEC usp_EvaluatePerformanceBy_Top_Max @pagesize=20,@pagenum=10000
时间为:319.800
 
 
--当读取第80万条附近的20条数据时花的时间为

EXEC usp_EvaluatePerformanceBy_Top_Max @pagesize=20,@pagenum=40000
所花的时间为:137.000
 
 

总结一下,当利用TOP和MAX来分页,且要查找的列都在索引中时,

1万条数据附近,为365.100ms

20万数据条附近,为319.800ms

80万条数据附近,为137.000ms

看一看最后的结论:


TOP 与 NOT IN


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

在SQLSERVER2005中优先选择ROW_NUMBER()方法来分页,在SQLSERVER 2000中优先选择TOP和NOT IN 方法!!

时间: 2024-12-09 04:56:02

sql 分页(原:http://www.cnblogs.com/fly_zj/archive/2010/07/06/1772536.html)的相关文章

http://www.cnblogs.com/huxi/archive/2010/07/04/1771073.html(转载)(原作者:AstralWind)

Python正则表达式指南 本文介绍了Python对于正则表达式的支持,包括正则表达式基础以及Python正则表达式标准库的完整介绍及使用示例.本文的内容不包括如何编写高效的正则表达式.如何优化正则表达式,这些主题请查看其他教程. 注意:本文基于Python2.4完成:如果看到不明白的词汇请记得百度谷歌或维基,whatever. 尊重作者的劳动,转载请注明作者及原文地址 >.<html 1. 正则表达式基础 1.1. 简单介绍 正则表达式并不是Python的一部分.正则表达式是用于处理字符串的

Android实现下拉导航选择菜单效果【转载地址:http://www.cnblogs.com/hanyonglu/archive/2012/07/31/2617488.html】

本文介绍在Android中如何实现下拉导航选择菜单效果.   关于下拉导航选择菜单效果在新闻客户端中用的比较多,当然也可以用在其他的项目中,这样可以很方便的选择更多的菜单.我们可以让我们的应用顶部有左右滑动或进行切换的导航菜单,也可以为了增强用户体验在应用中添加这样的下拉导航选择菜单效果. 关于它的实现原理,其实也是挺简单的,就是使用PopupWindow来进行展现,在显示时控制其高度并配置以相应的动画效果.在PopupWindow中我使用GridView来控制里面的菜单项,每个菜单项对应相应的

C#加密算法汇总(转载)http://www.cnblogs.com/zengxiangzhan/archive/2010/01/30/1659687.html

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 方法一:     //须添加对System.Web的引用     using System.Web.Security;          ...          /// <summary>     /// SHA1加密字符串     /// </summary>     /// <param name="source"

SQL Server 触发器 转http://www.cnblogs.com/hoojo/archive/2011/07/20/2111316.html

SQL Server 触发器 触发器是一种特殊类型的存储过程,它不同于之前的我们介绍的存储过程.触发器主要是通过事件进行触发被自动调用执行的.而存储过程可以通过存储过程的名称被调用. Ø 什么是触发器 触发器对表进行插入.更新.删除的时候会自动执行的特殊存储过程.触发器一般用在check约束更加复杂的约束上面.触发器和普通的存储过程的区别是:触 发器是当对某一个表进行操作.诸如:update.insert.delete这些操作的时候,系统会自动调用执行该表上对应的触发器.SQL Server 2

Linux 下 新增Oracle10g 实例 (转自http://www.cnblogs.com/lan0725/archive/2011/07/18/2109474.html)

主要分为5步:创建实例目录,创建密码文件,创建参数文件,创建建库脚本并建库,创建数据字典. 其中,需要特别注意2点: 目录的权限,即用户和所属用户组都要是oracle.可以切换到已存在的oracle用户或者以root创建,然后赋权. 创建实例的时候指定编码. 下面以 Oracle 10.2.0.1.0为例,开始创建: 说明: 此服务器上已经有一个正常运行的oracle实例cms,开机自启动,一个监听. 已有一个用户oracle,所属用户组oinstall. oracle环境变量:ORACLE_S

Oracle常用的一些 数据字典 转https://www.cnblogs.com/neozhu/archive/2008/07/22/1248422.html

Oracle常用数据字典表 查看当前用户的缺省表空间 SQL>select username,default_tablespace from user_users; 查看当前用户的角色 SQL>select * from user_role_privs; 查看当前用户的系统权限和表级权限 SQL>select * from user_sys_privs; SQL>select * from user_tab_privs; 查看用户下所有的表 SQL>select * from

ghostDoct 使用 (转 http://www.cnblogs.com/RockyMyx/archive/2010/04/20/Project-Route-Using-GhostDoc.html)

一.简介 GhostDoc是Visual Studio的一个免费插件,可以为开发人员自动生成XML格式的注释文档. 二.下载 需要的朋友可以去这里下载,填个Email地址就可以下了:GhostDoc下载地址 三.安装 下载安装完成后,可以在Visual Studio的工具菜单下找到GhostDoc的身影. 在第一次使用时,会要求设置快捷键,默认的是Ctrl+Shift+S,如果这和你设置的快捷键有所冲突的话,可以在选择的下拉列表里另外选择一个. GhostDoc使用的优点自然是可以快速生成注释,

XPath语法 在C#中使用XPath示例 【转http://www.cnblogs.com/yukaizhao/archive/2011/07/25/xpath.html】非常详细的文章

XPath语法 在C#中使用XPath示例 XPath可以快速定位到Xml中的节点或者属性.XPath语法很简单,但是强大够用,它也是使用xslt的基础知识. 示例Xml: <?xml version="1.0" encoding="utf-8" ?> <pets> <cat color="black" weight="10"> <price>100</price>

【HTML】HTML特殊符号【转http://www.cnblogs.com/web-d/archive/2010/04/16/1713298.html】

HTML特殊字符编码大全:往网页中输入特殊字符,需在html代码中加入以&开头的字母组合或以&#开头的数字.下面就是以字母或数字表示的特殊符号大全.                       ´ ´ © © > > µ µ ® ® & & ° ° ¡ ¡     » » ¦ ¦ ÷ ÷ ¿ ¿ ¬ ¬ § § • • ½ ½ « « ¶ ¶ ¨ ¨ ¸ ¸ ¼ ¼ < < ± ± × × ¢ ¢ ¾ ¾ ¯ ¯ “ " ™ ™ € € £