USE tempdb
GO
SET NOCOUNT ON --创建表结构
IF OBJECT_ID(N'ClassB', N'U') IS NOT NULL
DROP TABLE ClassB
GO
CREATE TABLE ClassB(ID INT PRIMARY KEY, Name VARCHAR(16), CreateDate DATETIME, AID INT, Status INT)
CREATE INDEX IDX_CreateDate ON ClassB(CreateDate)
CREATE INDEX IDX_AID ON ClassB(AID)
GO --插入测试数据
DECLARE @ID INT
SET @ID = 1
WHILE @ID <= 100000
BEGIN
INSERT INTO ClassB VALUES(@ID, 'fx', GETDATE(), @ID % 20, @ID % 20)
SET @ID = @ID + 1
END --统计总行数
SELECT 'ClassB' AS ClassB, count(1) AS Count FROM ClassB

查询条件如下:根据CreateDate倒序排序,CreateDate一致时按照ID倒序;时间区间在2014-07-13和2014-07-14之间;每页20条,当前页数第3页;

方案A:双Top,取出之前页的所有ID后,使用NOT IN排除这些ID进行查询,这是性能最差一种,因为他使用相同的查询谓词,查询了两次数据,且两次排序;

DECLARE @page_size INT = 20;
DECLARE @page_index INT = 3;
--A: 双Top:取出之前页的所有ID后,使用NOT IN排除这些ID进行查询,性能最差
WITH ID_List_Excluded AS
(
SELECT TOP (@page_size * (@page_index - 1)) ID FROM ClassB WHERE CreateDate BETWEEN '2014-07-13' AND '2014-07-14'
ORDER BY CreateDate DESC, ID DESC
)
SELECT TOP (@page_size) * FROM ClassB WHERE ID NOT IN (SELECT ID FROM ID_List_Excluded) AND CreateDate BETWEEN '2014-07-13' AND '2014-07-14'
ORDER BY CreateDate DESC, ID DESC;

表 'ClassB'。扫描计数 2,逻辑读取 136 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'Worktable'。扫描计数 1,逻辑读取 197 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

查询页数越靠后逻辑读取,下面是查询第30页的逻辑读取情况:

表 'ClassB'。扫描计数 2,逻辑读取 1243 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

表 'Worktable'。扫描计数 1,逻辑读取 2574 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

方案B:ROW_NUMBER,取出截止到当前页所有符合查询谓词的记录,并给每条记录加上序号,然后根据序号查询,性能次之;

DECLARE @page_size INT = 20;
DECLARE @page_index INT = 3;
--B: ROW_NUMBER:取出截至到当前页所有符合查询谓词的记录,并给每条记录加上序号,然后根据序号查询,性能次之
WITH NewClassB AS
(
SELECT TOP (@page_size * @page_index) ROW_NUMBER() OVER (ORDER BY CreateDate DESC, ID DESC) AS ROWID, * FROM ClassB
WHERE CreateDate BETWEEN '2014-07-13' AND '2014-07-14'
)
SELECT TOP (@page_size) * FROM NewClassB
WHERE ROWID BETWEEN (@page_size * (@page_index - 1) + 1) AND (@page_size * @page_index);

表 'ClassB'。扫描计数 1,逻辑读取 134 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

方案B已经比方案A快了近一倍,少了一个Worktable,同样,page_index越大,扫描次数越多,因为子查询中要查询多余的(之前页)数据和字段,下面是查询第30页时的IO读取情况;

表 'ClassB'。扫描计数 1,逻辑读取 1241 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

方案C:双Top,取出截止到当前页为止的所有的ID,去掉之前页的ID,然后根据ID(通常是主键)作常规查询,性能最优;

DECLARE @page_size INT = 20;
DECLARE @page_index INT = 3;
WITH ID_List AS
(
SELECT TOP (@page_size * @page_index) ID FROM ClassB
WHERE CreateDate BETWEEN '2014-07-13' AND '2014-07-14'
ORDER BY CreateDate DESC, ID DESC
)
,ID_List_Current AS
(
SELECT ID FROM ID_List WHERE ID NOT IN(SELECT TOP (@page_size * (@page_index - 1)) ID FROM ID_List)
)
SELECT * FROM ClassB WHERE ID IN (SELECT ID FROM ID_List_Current);

表 'ClassB'。扫描计数 2,逻辑读取 52 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

查询第30页的数据仅比查询第3页的数据多出两次逻辑读取,方案C和B的唯一在于:子查询中的查询列,方案B中返回了所有字段,所有要通过聚集索引走lookup,而方案C直接返回索引键即可。

表 'ClassB'。扫描计数 2,逻辑读取 54 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

SQL Server 中的三种分页方式的更多相关文章

  1. SQL Server中的三种Join方式

      1.测试数据准备 参考:Sql Server中的表访问方式Table Scan, Index Scan, Index Seek 这篇博客中的实验数据准备.这两篇博客使用了相同的实验数据. 2.SQ ...

  2. Asp.Net中的三种分页方式

    Asp.Net中的三种分页方式 通常分页有3种方法,分别是asp.net自带的数据显示空间如GridView等自带的分页,第三方分页控件如aspnetpager,存储过程分页等. 第一种:使用Grid ...

  3. 浅谈SQL Server中的三种物理连接操作

    简介 在SQL Server中,我们所常见的表与表之间的Inner Join,Outer Join都会被执行引擎根据所选的列,数据上是否有索引,所选数据的选择性转化为Loop Join,Merge J ...

  4. SQL Server中的三种物理连接操作

    来源:https://msdn.microsoft.com/zh-cn/library/dn144699.aspx 简介 在SQL Server中,我们所常见的表与表之间的Inner Join,Out ...

  5. 浅谈SQL Server中的三种物理连接操作(HASH JOIN MERGE JOIN NESTED LOOP)

    简介 在SQL Server中,我们所常见的表与表之间的Inner Join,Outer Join都会被执行引擎根据所选的列,数据上是否有索引,所选数据的选择性转化为Loop Join,Merge J ...

  6. 浅谈SQL Server中的三种物理连接操作(Nested Loop Join、Merge Join、Hash Join)

    简介 在SQL Server中,我们所常见的表与表之间的Inner Join,Outer Join都会被执行引擎根据所选的列,数据上是否有索引,所选数据的选择性转化为Loop Join,Merge J ...

  7. Asp.Net中的三种分页方式总结

    本人ASP.net初学,网上找了一些分页的资料,看到这篇文章,没看到作者在名字,我转了你的文章,只为我可以用的时候方便查看,2010的文章了,不知道这技术是否过期. 以下才是正文 通常分页有3种方法, ...

  8. Sql Server 常见的几种分页方式

    ⒈offset fetch next方式[SqlServer2012及以上版本支持][推荐] select * from T_User order by id offset rows /*(页数-1) ...

  9. SQL Server中数据库文件的存放方式,文件和文件组

    原文地址:http://www.cnblogs.com/CareySon/archive/2011/12/26/2301597.html   SQL Server中数据库文件的存放方式,文件和文件组 ...

随机推荐

  1. 63. Unique Paths II

    题目: Follow up for "Unique Paths": Now consider if some obstacles are added to the grids. H ...

  2. 7、SpringMVC源码分析(2):分析HandlerAdapter.handle方法,了解handler方法的调用细节以及@ModelAttribute注解

    从上一篇 SpringMVC源码分析(1) 中我们了解到在DispatcherServlet.doDispatch方法中会通过 mv = ha.handle(processedRequest, res ...

  3. 利用qt打开一张图片并转成灰度矩阵

    首先是mat类,这个类的主要作用是构造一个容器,并将对应像素的灰度值放在容器内 #ifndef MAT_H #define MAT_H #include <vector> #include ...

  4. 如何构建你自己的Java库【翻译】

    原文http://www.programcreek.com/2011/07/build-a-java-library-for-yourself/ 原文和翻译都只是参考,如有不对,欢迎指正. 代码复用是 ...

  5. HDU 4351 Digital root 线段树区间合并

    依然不是十分理解……待考虑…… #include <cstdio> #include <cstring> #include <cstdlib> #include & ...

  6. showdialog()与show的区别

    showdialog就是显示有模式的窗体,showdialog后面的语句不会执行,直到显示的窗体被关闭. show就是无模式的窗体,显示窗体后不论窗体是否关闭都执行show后面的语句. ------- ...

  7. 1218. Episode N-th: The Jedi Tournament(bfs)

    1218 简答题 对于当前点 判断每个点是否可达 #include <iostream> #include<cstdio> #include<cstring> #i ...

  8. css垂直水平居中方案

    1. 水平居中 如果是inline元素:在父元素上面设置text-align:center; 如果是block元素:设置宽度和margin:0 auto; 如果是多块级元素:在父元素上面设置text- ...

  9. Dataguard三种保护模式

    Oracle Data Guard 提供三种高水平的数据保护模式来平衡成本.可用性.性能和事务保护.可以使用任意可用管理界面来轻松地设置这些模式.要确定适当的数据保护模式,企业需要根据用户对系统响应时 ...

  10. 【MySQL for Mac】在Mac终端导入&导出.sql文件

    导入 打开终端输入:(前提是已经配置过MySQL环境变量) mysql -u root -p create database name; use name; source 『将.sql文件直接拖拽至终 ...