一直想整理下关于sql分页的几种方法,今天终于有时间整理下了。闲话少说直接上sql,先创建一个测试库,测试表以及测试数据,sql语句如下:

CREATE DATABASE DBTEST
GO
USE DBTEST
GO
--创建测试表
create table pagetest
(
id int identity(1,1) not null,
col01 int null,
col02 nvarchar(50) null,
col03 datetime null
) --100万记录集 耗时 3'46''
declare @i int
set @i=0
while(@i<1000000)
begin
insert into pagetest select cast(floor(rand()*1000000) as int),left(newid(),10),getdate()
set @i=@i+1
END

在pagetest表里面插入了1000000条记录,共耗时3'46'',当然这跟电脑配置有关。主要说以下几种方法:

为了方便定义两个参数,一个是页的大小@PageSize,一个是@PageIndex页的索引。

DECLARE @pageIndex INT, -----页索引

@pageSize INT  -----页大小

我们设置每页大小为500条,取@pageIndex=198 即199页数据,

SET @pageSize=500

SET @pageIndex=198

下面直接上几种常用的sql分页的方法:为了测试每种方法的执行时间,定义一个开始时间和结束时间

DECLARE @begin_date DATETIME;
DECLARE @end_date DATETIME;
SELECT @begin_date = GETDATE(); DECLARE @pageIndex INT, -----页索引
@pageSize INT -----页大小 SET @pageSize = 500;
SET @pageIndex = 198; ----写法一 NOT IN /Top
SET @begin_date = GETDATE(); SELECT TOP (@pageSize)
*
FROM dbo.pagetest
WHERE id NOT IN
(
SELECT TOP (@pageSize * @pageIndex) id FROM dbo.pagetest ORDER BY id ASC
)
ORDER BY id ASC; SELECT @end_date = GETDATE();
SELECT 'NOT IN /Top' AS 'Type',
DATEDIFF(ms, @begin_date, @end_date) AS '毫秒'; ----写法二 NOT EXIST
SET @begin_date = GETDATE(); SELECT TOP (@pageSize)
*
FROM dbo.pagetest p
WHERE NOT EXISTS
(
SELECT 1
FROM
(
SELECT TOP (@pageSize * @pageIndex)
*
FROM dbo.pagetest p
ORDER BY p.id ASC
) a
WHERE p.id = a.id
)
ORDER BY id ASC; SELECT @end_date = GETDATE();
SELECT 'NOT EXIST' AS 'Type',
DATEDIFF(ms, @begin_date, @end_date) AS '毫秒'; ----写法三 MAX/TOP
SET @begin_date = GETDATE(); SELECT TOP (@pageSize)
*
FROM dbo.pagetest
WHERE id >
(
SELECT MAX(id)
FROM
(
SELECT TOP (@pageSize * @pageIndex)
*
FROM dbo.pagetest
ORDER BY id ASC
) a
)
ORDER BY id ASC; SELECT @end_date = GETDATE();
SELECT 'MAX/TOP' AS 'Type',
DATEDIFF(ms, @begin_date, @end_date) AS '毫秒'; ----写法四 ROW NUMBER()
SET @begin_date = GETDATE(); SELECT TOP (@pageSize)
a.*
FROM
(
SELECT ROW_NUMBER() OVER (ORDER BY id ASC) rowNum,
*
FROM dbo.pagetest
) a
WHERE a.rowNum > (@pageSize * @pageIndex)
ORDER BY a.id; SELECT @end_date = GETDATE();
SELECT 'ROW NUMBER()一' AS 'Type',
DATEDIFF(ms, @begin_date, @end_date) AS '毫秒'; SET @begin_date = GETDATE(); SELECT a.*
FROM
(
SELECT ROW_NUMBER() OVER (ORDER BY id ASC) rowNum,
*
FROM dbo.pagetest
) a
WHERE a.rowNum > (@pageSize * @pageIndex)
AND a.rowNum < (@pageSize * @pageIndex + @pageSize + 1)
ORDER BY a.id; SELECT @end_date = GETDATE();
SELECT 'ROW NUMBER()二' AS 'Type',
DATEDIFF(ms, @begin_date, @end_date) AS '毫秒'; SET @begin_date = GETDATE(); SELECT a.*
FROM
(
SELECT ROW_NUMBER() OVER (ORDER BY id ASC) rowNum,
*
FROM dbo.pagetest
) a
WHERE a.rowNum
BETWEEN (@pageSize * @pageIndex + 1) AND (@pageSize * @pageIndex + @pageSize)
ORDER BY a.id; SELECT @end_date = GETDATE();
SELECT 'ROW NUMBER()三' AS 'Type',
DATEDIFF(ms, @begin_date, @end_date) AS '毫秒'; ----写法五 ROWNUMBER()变体
SET @begin_date = GETDATE(); SELECT *
FROM
(
SELECT ROW_NUMBER() OVER (ORDER BY tempColumn) AS rowNum,
*
FROM
(
SELECT TOP (@pageIndex * @pageSize + @pageSize)
tempColumn = 0,
*
FROM pagetest
WHERE 1 = 1
ORDER BY id ASC
) a
) b
WHERE b.rowNum > (@pageIndex * @pageSize); SELECT @end_date = GETDATE();
SELECT 'ROW NUMBER()变体' AS 'Type',
DATEDIFF(ms, @begin_date, @end_date) AS '毫秒'; ----写法六 OFFSET mssql2012后支持
SET @begin_date = GETDATE();
SELECT *
FROM dbo.pagetest
ORDER BY id ASC
OFFSET @pageIndex * @pageSize ROWS FETCH NEXT @pageSize ROWS ONLY; SELECT @end_date = GETDATE();
SELECT 'OFFSET' AS 'Type',
DATEDIFF(ms, @begin_date, @end_date) AS '毫秒';

大体看一下几种方法的执行速度,

执行多次后大体可以看出Max Top,RowNum()以及offset的执行效率比较高,RowNum()应该是在sql2005后就支持了,而offset是从sql2012开始支持。将以上两种常用的方法封装成存储过程:一个是RowNum() 一个是MaxTop

USE [DBTEST]
GO /****** Object: StoredProcedure [dbo].[Proc_SqlPageByRownumber] Script Date: 2017/4/21 16:24:52 ******/
SET ANSI_NULLS ON
GO SET QUOTED_IDENTIFIER ON
GO -- =============================================
-- Author: Allen
-- Create date: 2017-04-19
-- Description: 根据RowNumber()分页
-- =============================================
CREATE PROCEDURE [dbo].[Proc_SqlPageByRownumber]
(
@tbName VARCHAR(255), --表名
@tbGetFields VARCHAR(1000) = '*', --返回字段
@OrderfldName VARCHAR(255), --排序的字段名
@PageSize INT = 20, --页尺寸
@PageIndex INT = 1, --页码
@OrderType BIT = 0, --0升序,非0降序
@strWhere VARCHAR(1000) = '', --查询条件
@TotalCount INT OUTPUT --返回总记录数
)
AS
DECLARE @strSql VARCHAR(5000); --主语句
DECLARE @strSqlCount NVARCHAR(500); --查询记录总数主语句
DECLARE @strOrder VARCHAR(300); -- 排序类型
BEGIN
--------------总记录数---------------
IF ISNULL(@strWhere, '') <> ''
SET @strSqlCount = 'Select @TotalCout=count(1) from ' + @tbName + ' where 1=1 ' + @strWhere;
ELSE
SET @strSqlCount = 'Select @TotalCout=count(1) from ' + @tbName; exec sp_executesql @strSqlCount,N'@TotalCout int output',@TotalCount output
--------------分页------------
IF @PageIndex <= 0
SET @PageIndex = 1; IF (@OrderType <> 0)
SET @strOrder = ' ORDER BY ' + @OrderfldName + ' DESC ';
ELSE
SET @strOrder = ' ORDER BY ' + @OrderfldName + ' ASC '; SET @strSql
= 'SELECT * FROM
(SELECT ROW_NUMBER() OVER(' + @strOrder + ') RowNo,' + @tbGetFields + ' FROM ' + @tbName + ' WHERE 1=1 '
+ @strWhere + ' ) tb
WHERE tb.RowNo BETWEEN ' + STR((@PageIndex - 1) * @PageSize + 1) + ' AND ' + STR(@PageIndex * @PageSize); EXEC (@strSql); SELECT @TotalCount END; GO
USE [DBTEST]
GO /****** Object: StoredProcedure [dbo].[ProcSqlPageByMaxTop] Script Date: 2017/4/21 16:27:11 ******/
SET ANSI_NULLS ON
GO SET QUOTED_IDENTIFIER ON
GO -- =============================================
-- Author: Allen
-- Create date: 2017-04-19
-- Description: MaxTop
-- =============================================
CREATE PROCEDURE [dbo].[ProcSqlPageByMaxTop]
@tbName VARCHAR(255), --表名
@tbFields VARCHAR(1000), --返回字段
@PageSize INT, --页尺寸
@PageIndex INT, --页码
@strWhere VARCHAR(1000), --查询条件
@StrOrder VARCHAR(255), --排序条件
@Total INT OUTPUT --返回总记录数
AS
DECLARE @strSql VARCHAR(5000); --主语句
DECLARE @strSqlCount NVARCHAR(500); --查询记录总数主语句
BEGIN
--------------总记录数---------------
IF @strWhere != ''
BEGIN
SET @strSqlCount = 'Select @TotalCout=count(*) from ' + @tbName + ' where ' + @strWhere;
END;
ELSE
BEGIN
SET @strSqlCount = 'Select @TotalCout=count(*) from ' + @tbName;
END;
--------------分页------------
IF @PageIndex <= 0
BEGIN
SET @PageIndex = 1;
END; SET @strSql
= 'select top ' + STR(@PageSize) + ' * from ' + @tbName + '
where id>(select max(id) from (select top ' + STR((@PageIndex - 1) * @PageSize) + ' id from ' + @tbName + ''
+ @StrOrder + ')a)
' + @StrOrder + ''; EXEC sp_executesql @strSqlCount, N'@TotalCout int output', @Total OUTPUT;
EXEC (@strSql);
END; GO
ish

以上也只是对单表的查询,注意在sql优化时要加上主键和索引,查询效率会提高,关于如何创建索引,在哪些字段时创建索引在下一篇博客中说明。

sql分页的几种写法的更多相关文章

  1. sql 分页的两种写法

    string Strsql = string.Format(@"select ee.DOCUMENTNO,ee.APPLICANTNAME,ee.COMPANY,ee.REQUESTTIME ...

  2. sql语句有几种写法

    sql语句有几种写法 1:SELECT * FROM tablename ORDER BY RAND() LIMIT 想要获取的数据条数: 2:SELECT *FROM `table` WHERE i ...

  3. Sql函数的三种写法

    以前复制的创建sql函数比较乱,现在将我自己项目中的三种sql函数做下对比,一目了然: (1)表值函数——方法一:直接创建临时表,并返回临时表.优点:函数体中间可以直接申明临时变量,并做各种逻辑处理, ...

  4. SQL分页的几种方式

    1.使用Row_number() over(order by columnName)函数来作为标示分页(下面的例子都是以last_seen来排序的,要求取顺序为20-30行的数据) SELECT Us ...

  5. SQL 循环语句几种写法

    1.正常循环语句 declare @orderNum varchar(255)create table #ttableName(id int identity(1,1),Orders varchar( ...

  6. 查询分页的几种Sql写法

    查询分页的几种Sql写法 摘自:http://www.cnblogs.com/zcttxs/archive/2012/04/01/2429151.html 1.创建测试环境,(插入100万条数据大概耗 ...

  7. 服务器文档下载zip格式 SQL Server SQL分页查询 C#过滤html标签 EF 延时加载与死锁 在JS方法中返回多个值的三种方法(转载) IEnumerable,ICollection,IList接口问题 不吹不擂,你想要的Python面试都在这里了【315+道题】 基于mvc三层架构和ajax技术实现最简单的文件上传 事件管理

    服务器文档下载zip格式   刚好这次项目中遇到了这个东西,就来弄一下,挺简单的,但是前台调用的时候弄错了,浪费了大半天的时间,本人也是菜鸟一枚.开始吧.(MVC的) @using Rattan.Co ...

  8. [转]几种常见SQL分页方式

    创建环境: create table pagetest ( id ,) not null, col01 int null, col02 ) null, col03 datetime null ) -- ...

  9. .net连接sql server的几种连接字符串的写法

    .net连接sql server的几种连接字符串的写法 1, 混合验证模式登录 server=电脑名 或 电脑IP;database=数据库名;uid=数据库登录名;password=数据库登录密码 ...

随机推荐

  1. 让vs只启动自己想调试的站点

    VS中里面多个WEB项目如何只启动一个? 每次启动时,右下角都会出现一堆的 网站有10来个.即使设置了默认启动项目, 但每次按F5启动,或者哪怕是在项目上右键启动新实例 右下角都会出现这一堆的站点 有 ...

  2. 介绍一下except的用法和作用?

    Python的except用来捕获所有异常,因为Python里面的每次错误都会抛出一个异常,所以每个程序的错误都被当作一个运行时错误.

  3. centos6.9下php7安装zip扩展

    cd /usr/local/src wget http://pecl.php.net/get/zip-1.13.5.tgz tar -zxvf zip-1.13.5.tgz cd zip-1.13.5 ...

  4. MySQL如何优化GROUP BY :松散索引扫描 VS 紧凑索引扫描

    执行GROUP BY子句的最一般的方法:先扫描整个表,然后创建一个新的临时表,表中每个组的所有行应为连续的,最后使用该临时表来找到组 并应用聚集函数.在某些情况中,MySQL通过访问索引就可以得到结果 ...

  5. Js全局异常捕获

    重新window.onerror方法就行了 window.onerror = handleError function handleError(msg,url,l) { var txt="T ...

  6. shuit模块

    shuit模块 #高级的 文件.文件夹.压缩包 处理模块 shutil.copyfileobj(fsrc, fdst[, length])将文件内容拷贝到另一个文件中,可以部分内容 def copyf ...

  7. OVF and OVA

    最近测试的东西有关于ovf 和ova等相关用例,在网上找了点内容摘抄了下来. 一.什么是OVF文件 开源虚拟化格式OVF文件是一种开源的文件规范,它描述了一个开源.安全.有效.可拓展的便携式虚拟打包以 ...

  8. $.messager.confirm修改弹出框按钮提示文字

    $.messager.confirm 默认提示语为“OK”和“Cancel”.引入中文控件后变为“确定”和“取消” <script src="../js/locale/easyui-l ...

  9. 流量分析系统---redis

    1\启动redis 方法一: 修改了某些配置,具体步骤惨开下面的内容 [root@mini1 ~]# service redis stop/start 方法二: [root@mini1 bin]#cd ...

  10. Ajax+Spring MVC实现跨域请求(JSONP)

    背景: AJAX向后台(springmvc)发送请求,报错:已阻止交叉源请求:同源策略不允许读取 http://127.0.0.1:8080/DevInfoWeb/getJsonp 上的远程资源.可 ...