http://www.cnblogs.com/qqlin/archive/2012/11/01/2745161.html

1.引言

在列表查询时由于数据量非常多,一次性查出来会非常慢,就算一次查出来了,也不能一次性显示给客户端,所以要把数据进行分批查询出来,每页显示一定量的数据,这就是数据要分页。

2.常用的数据分页方法

我们经常会碰到要取n到m条记录,就是有分页思想,下面罗列一下一般的方法。

我本地的一张表 tbl_FlightsDetail,有300多W记录,主键 FlightsDetailID(Guid),要求按照FlightsDetailID排序 取 3000001 到3000010 之间的10条记录,也是百万级。

方法1 定位法 (利用ID大于多少)

语句形式:

select top 10 * from tbl_FlightsDetail where FlightsDetailID>(
select max(FlightsDetailID) from (
select top 3000000 FlightsDetailID from tbl_FlightsDetail order by FlightsDetailID
) as t
) order by FlightsDetailID

执行计划:

先查出 top 300000,再聚合取这个集合中最大的Id1,再过滤 id大于id1的集合(上图中使用到索引),再取top 10 条。

方法2 (利用Not In)

语句形式:

select top 10* from tbl_FlightsDetail where FlightsDetailID not in (
select top 3000000 FlightsDetailID from tbl_FlightsDetail order by FlightsDetailID
) order by FlightsDetailID

执行计划:

和方法一类似,只是过滤where条件不一样,这里用到的是not in,上图中没有用到索引,耗时8秒。如果 FlightsDetailID不是索引的话,方法1和该方法将差不多。

方法3 (利用颠颠倒倒top)

语句形式:

select top 10* from (
select top 3000010* from tbl_FlightsDetail order by FlightsDetailID
) as t order by t.FlightsDetailID desc

执行计划:

先取 前面3000010条记录,再倒序,这时再取前面10条即是300001 到300010条记录,没有用到索引,耗时11秒

方法4 (ROW_NUMBER()函数)

语句形式:

select * from (
select *,ROW_NUMBER() OVER (ORDER BY FlightsDetailID) as rank from tbl_FlightsDetail
) as t where t.rank between 3000001 and 3000010

执行计划:

Sql 2005版本或以上支持,也没用到索引,耗时2秒,速度还不错。

方法5 (利用IN)

此方法是由 金色海洋(jyk)阳光男孩 回复的,飞常感谢,语句形式:

select top 10 * from tbl_FlightsDetail  where FlightsDetailID in(
select top 10 FlightsDetailID from(
select top 3000010 FlightsDetailID from tbl_FlightsDetail order by FlightsDetailID
) as t order by t.FlightsDetailID desc
) order by FlightsDetailID

执行计划:

多次执行之后一般维持在4秒左右,用到索引,非常不错,计划图还很长,只截取部分,可能是绕的多一点。

3.千万级分页存储过程

大家百度一下这个标题立马会出现很多相关信息,都大同小异,我自己拷贝的一个,应项目的需要,修改了一个排序的bug以及添加了返回总记录数,如下:

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
--分页存储过程
CREATE PROCEDURE [dbo].[sp_Paging]
(
@Tables nvarchar(1000), --表名/视图名
@PrimaryKey nvarchar(100), --主键
@Sort nvarchar(200) = NULL, --排序字段(不带order by)
@pageindex int = 1, --当前页码
@PageSize int = 10, --每页记录数
@Fields nvarchar(1000) = N'*', --输出字段
@Filter nvarchar(1000) = NULL, --where过滤条件(不带where)
@Group nvarchar(1000) = NULL, --Group语句(不带Group By)
@TotalCount int OUTPUT --总记录数
)
AS DECLARE @SortTable nvarchar(100)
DECLARE @SortName nvarchar(100)
DECLARE @strSortColumn nvarchar(200)
DECLARE @operator char(2)
DECLARE @type nvarchar(100)
DECLARE @prec int --设定排序语句
IF @Sort IS NULL OR @Sort = ''
SET @Sort = @PrimaryKey
IF CHARINDEX('DESC',@Sort)>0
BEGIN
SET @strSortColumn = REPLACE(@Sort, 'DESC', '')
SET @operator = '<='
END
ELSE
BEGIN
SET @strSortColumn = REPLACE(@Sort, 'ASC', '')
SET @operator = '>='
END
IF CHARINDEX('.', @strSortColumn) > 0
BEGIN
SET @SortTable = SUBSTRING(@strSortColumn, 0, CHARINDEX('.',@strSortColumn))
SET @SortName = SUBSTRING(@strSortColumn, CHARINDEX('.',@strSortColumn) + 1, LEN(@strSortColumn))
END
ELSE
BEGIN
SET @SortTable = @Tables
SET @SortName = @strSortColumn
END --设置排序字段类型和精度
SELECT @type=t.name, @prec=c.prec FROM sysobjects o
JOIN syscolumns c on o.id=c.id
JOIN systypes t on c.xusertype=t.xusertype WHERE o.name = @SortTable AND c.name = @SortName IF CHARINDEX('char', @type) > 0
SET @type = @type + '(' + CAST(@prec AS varchar) + ')' DECLARE @strPageSize nvarchar(50)
DECLARE @strStartRow nvarchar(50)
DECLARE @strFilter nvarchar(1000)
DECLARE @strSimpleFilter nvarchar(1000)
DECLARE @strGroup nvarchar(1000) IF @pageindex <1
SET @pageindex = 1
SET @strPageSize = CAST(@PageSize AS nvarchar(50))
--设置开始分页记录数
SET @strStartRow = CAST(((@pageindex - 1)*@PageSize + 1) AS nvarchar(50))
--筛选以及分组语句
IF @Filter IS NOT NULL AND @Filter != ''
BEGIN
SET @strFilter = ' WHERE ' + @Filter + ' '
SET @strSimpleFilter = ' AND ' + @Filter + ' '
END
ELSE
BEGIN
SET @strSimpleFilter = ''
SET @strFilter = ''
END
IF @Group IS NOT NULL AND @Group != ''
SET @strGroup = ' GROUP BY '
--计算总记录数
DECLARE @TotalCountSql nvarchar(1000)
SET @TotalCountSql=N'SELECT @TotalCount=COUNT(*)' +N' FROM ' + @Tables + @strFilter
EXEC sp_executesql @TotalCountSql,N'@TotalCount int OUTPUT',@TotalCount OUTPUT
--执行查询语句
EXEC(
'
DECLARE @SortColumn ' + @type + '
SET ROWCOUNT ' + @strStartRow + '
SELECT @SortColumn=' + @strSortColumn + ' FROM ' + @Tables + @strFilter + ' ' + @strGroup + ' ORDER BY ' + @Sort + '
SET ROWCOUNT ' + @strPageSize + '
SELECT ' + @Fields + ' FROM ' + @Tables + ' WHERE ' + @strSortColumn + @operator + ' @SortColumn ' + @strSimpleFilter + ' ' + @strGroup + ' ORDER BY ' + @Sort + '
'
)

现在我们来测试一下:

DECLARE    @return_value int,
@TotalCount int EXEC @return_value = [dbo].[sp_Paging]
@Tables = N'tbl_FlightsDetail',
@PrimaryKey = N'FlightsDetailID',
@Sort = N'FlightsDetailID',
@pageindex = 299999,
@PageSize = 10,
@Fields = '*',
@Filter = NULL,
@Group = NULL,
@TotalCount = @TotalCount OUTPUT SELECT @TotalCount as N'@TotalCount' SELECT 'Return Value' = @return_value

执行计划:

看时间的确是快,执行计划显示4个查询

查询1,是利用系统表获取排序字段、类型和精度,这个很快,全是索引。

查询2,返回总记录数,第一次会慢点,后面就很快了。

查询3 和查询4(用到索引) 才是我们要分页取的数据,查询3 是排序,取一个最大的值赋给变量,查询4是大于这个变量的值 取数据,直接看sql语句,把上面的exec动态语句改成如下:

DECLARE @SortColumn varchar(40)
--即 top 3000001,取出最大的 id覆盖@SortColumn
SET ROWCOUNT 3000001
SELECT @SortColumn= FlightsDetailID FROM tbl_FlightsDetail ORDER BY FlightsDetailID
--即 top 10
SET ROWCOUNT 10
SELECT * FROM tbl_FlightsDetail WHERE FlightsDetailID >= @SortColumn ORDER BY FlightsDetailID

你会发现,原来它跟我们标题2 常用的数据分页方法 中的 方法1 定位 类似,原来奥秘在这。

4.小结

还有一些用游标、表变量的那个性能差不作考虑。分页存储过程看起来挺复杂的,语句多,其实都在判断,在左组装,右组装,最终组装成类似 标题2中常用的分页方法中的 的一种语句,掌握了常用的数据分页方法,大家就可以自己写了,当然还有其它的方法,大家可以分享出来。

Sql Server 数据分页的更多相关文章

  1. SQL Server 数据分页查询

    最近学习了一下SQL的分页查询,总结了以下几种方法. 首先建立了一个表,随意插入的一些测试数据,表结构和数据如下图: 现在假设我们要做的是每页5条数据,而现在我们要取第三页的数据.(数据太少,就每页5 ...

  2. (sql server)数据分页的实现

    谈谈自己了解的几种数据库分页的方法,下面來分享下,有什么好的方法可以指导一下哦.. 方法一:利用ROW_NUMBER()方法 利用ROW_NUMBER 产生序列后直接取出 /*如要查詢的表為Test ...

  3. sql server 数据分页显示。

    select [ID] ,[StockApplyCode] ,[RcCode] ,[LabCenterCode] ,[LabGroupCode] ,[LabGroupName] ,[Barcode] ...

  4. 浅谈SQL Server数据库分页

    数据库分页是老生常谈的问题了.如果使用ORM框架,再使用LINQ的话,一个Skip和Take就可以搞定.但是有时由于限制,需要使用存储过程来实现.在SQLServer中使用存储过程实现分页的已经有很多 ...

  5. SQL server数据缓存依赖

    SQL server数据缓存依赖有两种实现模式,轮询模式,通知模式. 1  轮询模式实现步骤 此模式需要SQL SERVER 7.0/2000/2005版本以上版本都支持        主要包含以下几 ...

  6. [SQL]SQL Server数据表的基础知识与增查删改

    SQL Server数据表的基础知识与增查删改 由张晨辉(学生) 于19天 前发表 | 阅读94次 一.常用数据类型 .整型:bigint.int.smallint.tinyint .小数:decim ...

  7. Sql Server数据的加密与解密

    Sql Server数据的加密与解密 在sql server中,我们如何为数据进行加密与解密,避免使用者窃取机密数据? 对于一些敏感数据,如密码.卡号,一般不能使用正常数值来存储.否则会有安全隐患.以 ...

  8. delphi 2010 导出sql server 数据到DBF乱码问题

    近日,由于业务需要导出sql server 数据到DBF文件,要查询多表记录,并适当处理后生成导出DBF文件,系统使用delphi2010平台开发. 首先按要求在VFP里创建DBF表,字段数有240个 ...

  9. SQL server数据库内置账户SA登录设置

    SQL server数据库内置账户SA登录不了   设置SQL Server数据库给sa设置密码的时候  提示18456 解决步骤:   第二步:右击sa,选择属性: 第三步:点击状态选项卡:勾选授予 ...

随机推荐

  1. SAX和DOM解析的区别

    XML和JSon是ios解析文件的两种形式, 两种方法各有千秋. 1>. XML分为SAX和DOM两种方式 SAX是按顺序逐行读取文件, 查找到符合条件的内容时就会停止, 而DOM是讲内容一次性 ...

  2. Mongodb 副本集分片(二)---mongodb副本集部署脚本详解

    分享下,最近做的一主一从一仲裁的示例,如有需要,大家可以扩展成一主两从一仲裁. 大家可以看到  我的集群名字沿用了默认的neunnm,如果是其他的话   大家注意修改. 需要辅助文件hosts.con ...

  3. 如何用fir.im 命令行工具 打包上传

    1.注册fir.拿到token 2.安装 fir-cli 使用 Ruby 构建, 无需编译, 只要安装相应 gem 即可. $ ruby -v # > 1.9.3 $ gem install f ...

  4. ADO总结测试数据库

    create database ADO测试 go use ADO测试 go create table Student ( Code ) not null primary key,--学生编号,主键 N ...

  5. [转载] linux查找目录下的所有文件中是否含有某个字符串

    链接自 http://blog.sina.com.cn/s/blog_691a84f301015khx.html,并略加修订. 查找目录下的所有文件中是否含有某个字符串 find .|xargs gr ...

  6. IOS开发_中遍历数组的方法及比较

    数组,做为一种常用的数据类型,频繁出现在编码中,其中肯定少不了对数组的遍历,本博文对数组遍历,进行一下自己的归纳,如果是大牛,一笑而过就好,互相学习,欢迎指正. 话不多说直接进入主题 首先创建一个数组 ...

  7. easyui-validatebox 验证

    required: "必选字段",        remote: "请修正该字段",        email: "请输入正确格式的电子邮件" ...

  8. 使用AjaxPro实现无刷新更新数据

    需求 在一个页面动态无刷新的更新后台得到的数据.要想无刷新的更新数据,需要使用Javascript能够获取后台返回的数据,然后通过第三方Javascript库(JQuery等)动态更新web页面DOM ...

  9. 移动互联网公司如何将BPM流程管理变身移动化?

    背景介绍 OPPO是广东欧珀移动通信有限公司的旗下品牌,成立于2004年,是一家全球性的智能终端和移动互联网公司,致力于为客户提供最先进和最精致的智能手机.高端影音设备和移动互联网产品与服务,业务覆盖 ...

  10. eap-peap/mschapv2

    eap-peap/mschapv2       文件路径 用途 示例 备注 #gedit /usr/local/etc/raddb/sites-available/default #gedit /us ...