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中常用的分页方法中的 的一种语句,掌握了常用的数据分页方法,大家就可以自己写了,当然还有其它的方法,大家可以分享出来。

作者:Qlin 
出处:http://www.cnblogs.com/qqlin/ 
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利

sql server分页查询的更多相关文章

  1. SQL Server分页查询方法整理

    SQL Server数据库分页查询一直是SQL Server的短板,闲来无事,想出几种方法,假设有表ARTICLE,字段ID.YEAR...(其他省略),数据53210条(客户真实数据,量不大),分页 ...

  2. SQL Server分页查询进化史

    分页查询一直SQL Server的一个硬伤,就是是经过一些进化,比起MySql的limit还是有一些差距. 一.条件过滤(适应用所有版本) 条件过滤的方法有很多,而思路就是利用集合的差集选择出目标集合 ...

  3. SQL SERVER 分页查询

    Sqlserver数据库分页查询一直是Sqlserver的短板. 但现在不是了. 自从有了它. 一口气上十楼. 官方语法说明示例: https://technet.microsoft.com/zh-c ...

  4. Oracle分页查询和SQL server分页查询总结

    分页查询是项目中必不可少的一部分,难倒是不难,就是这些东西,长时间不用,就忘的一干二净了.今天特此总结一下这两款数据库分页查询的实现过程(只记录效率比较高的) 一.Oracle中的分页查询 1.通用分 ...

  5. SQL Server分页查询存储过程

    --分页存储过程create PROCEDURE [dbo].[commonPagination]@columns varchar(500), --要显示的列名,用逗号隔开 @tableName va ...

  6. 最佳sql server 分页查询

    用关键字查询 并作为条件是最快的,比其他嵌套select性能都要好 select top 100 id , name from tablex where id >preid order by i ...

  7. SQL Server分页查询的万能存储过程

    CREATE proc [dbo].[p_paging]@tableName varchar(8000), --表名.视图名@indexCol varchar(50) = 'id', --标识列名(如 ...

  8. Oracle/MySql/SQL Sqlserver分页查询

    简述 简单概括一下Oracle,MySql,SQL Sqlserver这三个数据库的分页查询语句. Oracle分页查询 例:每页显示两条数据,现在要查询第二页,也就是第3-4条数据. 查询语句: s ...

  9. SQL 数据分页查询

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

随机推荐

  1. NGrinder操作指南

    一.使用nGrinder谷歌插件录制性能测试脚本: 1.下载录制插件压缩包ngrinder-recorder-1.0.7z,解压到本地目录. 2.打开Chrome浏览器,URL框内输入chrome:/ ...

  2. 深入了解java虚拟机(JVM) 第五章 如何判断对象为垃圾对象

    本章开始学习垃圾回收的过程,垃圾回收的过程首先就是要确定对象是否是垃圾对象,如果是垃圾对象,垃圾回收器才会进行回收.垃圾回收主要又两种算法:引用计数算法和可达性分析算法. 一.引用计数算法 引用计数算 ...

  3. vue数据响应的坑

    1.首先遇到的第一个坑是数组 vue初始化时,data是一个数组并且为空的时候,里面有一些对象元素,直接改变这些对象的的属性不会触发视图更新 解决办法,copy一个新的数组(vue.assign是浅c ...

  4. P4491 [HAOI2018]染色 容斥+NTT

    $ \color{#0066ff}{ 题目描述 }$ 为了报答小 C 的苹果, 小 G 打算送给热爱美术的小 C 一块画布, 这块画布可 以抽象为一个长度为 \(N\) 的序列, 每个位置都可以被染成 ...

  5. [web]深入理解Session和Cookie

    一.理解Cookie 由于http是一种无状态的协议,当用户的一次访问结束后,后端的服务器就无法知道下一次来访问的请求是不是上一次的用户了.那么Cookie的作用就是用户通过http访问一个服务器时, ...

  6. RN_ 错误整理

    1. this.setState is not a function   或者 this.setState is undefined 在 constructor 中加入 this.select = t ...

  7. python高级(一)—— python数据模型(特殊方法)

    本文主要内容 collections.namedtuple __getitem__ 和 __len__ __repr__和__str__ __abs__.__add__和__mul__ __bool_ ...

  8. python学习,day2:字典

    字典的增删改查 # coding=utf-8 # Author: RyAn Bi info = { 'stu1101':'Tenglan Wu', 'stu1102':'longze Luola', ...

  9. element-ui 使用span-method表格合并后hover样式的处理

    在使用element表格合并后,发现鼠标只有移入第一个合并行时,合并的部分会高亮,移入其他行,不会高亮,这样效果看起来不是很好.查看了文档也没有直接的解决方法,就通过现有的方法处理了一下,解决了hov ...

  10. Windows操作系统的发展简介

    一.简介    Microsoft Windows,是美国微软公司研发的一套操作系统,它问世于1985年,起初仅仅是Microsoft-DOS模拟环境,后续的系统版本由于微软不断的更新升级,不但易用, ...