07. 分页写法小结

本文涉及的产品
云数据库 RDS SQL Server,独享型 2核4GB
简介: 原文:07. 分页写法小结分页的实现方式有这几种:1. 在前台程序中,将所有的记录都读到本地,前台程序通过游标在数据集中上下移动,数据量大的话,性能很差,不推荐;2. 前台程序请求某一页数据时,到数据库做一次查询,返回符合条件的相应记录,这也是目前常用的方法;3. 对方式2的改进,当请求某一页时,同时将前后几页一并返回,用户翻页时就不需要反复请求数据库了。
原文: 07. 分页写法小结

分页的实现方式有这几种:
1. 在前台程序中,将所有的记录都读到本地,前台程序通过游标在数据集中上下移动,数据量大的话,性能很差,不推荐
2. 前台程序请求某一页数据时,到数据库做一次查询,返回符合条件的相应记录,这也是目前常用的方法
3. 对方式2的改进,当请求某一页时,同时将前后几页一并返回,用户翻页时就不需要反复请求数据库了。


对于2,3的实现,随着SQL Server版本的升级,常用的方法有三种:TOP,ROW_NUMBER,OFFSET/FETCH NEXT。
测试数据:

if OBJECT_ID('split_pages') is not null
drop table split_pages
GO
create table split_pages 
(
ID int primary key,
Name varchar(100) 
)
GO
declare @i int
set @i=1
while @i<=300
begin
insert into split_pages
select @i,'TEST' + CAST(@i as varchar(10))
set @i=@i+10
end
GO
select * from split_pages

一. TOP

SQL Server 2000时多用,另外,有人用过set rowcount来分页,原理类似TOP,不过需要反复设置set rowcount,不方便,而且set rowcount中的行数不能参与select语句的执行计划,只能起到控制行数的作用。
1. 哪种TOP写法更高效
(1) 利用order by正反排序

select * 
from (select top 10 * 
from (select top 20 * from split_pages order by ID) a 
order by ID desc) b 
order by ID

公式为:

declare @page_no int
declare @page_size int

select * 
from (select top @page_size * 
from (select top @page_size*@page_no * from split_pages order by ID) a 
order by ID desc) b 
order by ID

做完最里层select后,再对派生表查询时,index就没有效果了,而且越往后面要top更多的数据,这种写法会更慢。

 

(2) 利用NOT IN或者NOT EXISTS

select top 10 *
from split_pages
where ID NOT IN (select top 10 ID from split_pages order by ID)
order by ID

公式为:

declare @page_no int
declare @page_size int

select top @page_size *
from split_pages
where ID NOT IN (select top @page_size*(@page_no-1) ID from split_pages order by ID)
order by ID

通常在写SQL语句时,用IN/EXISTS不一样,如果逻辑不变的话, EXISTS的效率高。

不过,利用NOT IN分页,和用NOT EXISTS效果基本一样,因为都需要扫完全部数据。

 

(3) 利用ID大于MAX(ID)

select top 10 *
from split_pages
where ID > (select MAX(ID) from (select top 10 ID from split_pages order by ID) t)
order by ID

公式为:

declare @page_no int
declare @page_size int

select top @page_size *
from split_pages
where ID > (select MAX(ID) from (select top @page_size*(@page_no-1) ID from split_pages order by ID) t)
order by ID

在使用TOP分页时,这种用法效率最高。

 

2. SQL Server 2000与2005中TOP的区别
(1) SQL Server 2000时,top不支持变量,所以分页时,这些语句都要改成动态SQL
如下:

declare @page_no int
declare @page_size int
declare @sql varchar(8000)

set @page_no = 2
set @page_size = 10
set @sql = 
'select top ' + str(@page_size) + '*
from split_pages ' + '
where ID > (select MAX(ID) from (select top ' + str(@page_size*(@page_no-1))+ ' ID from split_pages order by ID) t)
order by ID'
exec(@sql)

 

(2) SQL Server 2005开始,top可以直接使用变量

如下:

declare @page_no int
declare @page_size int
set @page_no = 2
set @page_size = 10
select top (@page_size) *
from split_pages
where ID NOT IN (select top (@page_size*(@page_no-1)) ID from split_pages order by ID)
order by ID

 

二. ROW_NUMBER
SQL Server 2005开始的新语法,和ORACLE,DB2中的row_number()类似。性能比用TOP有所提升。

在利用ROW_NUMBER分页时,总页数/行数的计算,可以有这几种写法。
(1) 单独的SQL语句去获得总行数

select COUNT(*) AS TotRows
from split_pages
GO
declare @page_no int
declare @page_size int

set @page_no = 2
set @page_size = 10

;with tmp
AS
(
select *,
ROW_NUMBER() OVER(order by ID) num
from split_pages
)
select ID, Name
from tmp
where num BETWEEN (@page_size*(@page_no-1)+1) AND @page_size*@page_no
order by num

 

(2) 在ROW_NUMBER的同时用COUNT计算总行数

declare @page_no int
declare @page_size int

set @page_no = 2
set @page_size = 10

;WITH tmp
AS
(
select *, 
ROW_NUMBER() OVER(order by ID) num, 
COUNT(*) OVER() total
from split_pages
)
select ID, Name
from tmp
where num BETWEEN (@page_size*(@page_no-1)+1) AND @page_size*@page_no
order by num

 

(3) 仅使用ROW_NUMBER计算总行数,IO最少

declare @page_no int
declare @page_size int

set @page_no = 2
set @page_size = 10

;with tmp
as
(
select *, 
ROW_NUMBER() OVER(order by ID) num, 
ROW_NUMBER() OVER(order by ID desc) num_desc
from split_pages
)
select ID, Name, num_desc + num -1 as total
from tmp
where num BETWEEN (@page_size*(@page_no-1)+1) AND @page_size*@page_no
order by num

 

三. OFFSET/FETCH NEXT
SQL Server 2012的新语法,类似MYSQL,POSTGRESQL中的LIIMIT/OFFSET,据称性能比ROW_NUMBER又有了提升。

declare @page_no int
declare @page_size int

set @page_no = 3
set @page_size = 10

SELECT *,COUNT(*) OVER() AS Total
FROM split_pages
ORDER BY ID
OFFSET (@page -1) * @size ROWS
FETCH NEXT @size ROWS ONLY;

 

四. 其他方法
(1) 临时表
在SQL Server 2000时,利用临时表生成rownum来分页,不推荐。

select IDENTITY(int,1,1) as num,* into #t from split_pages
select * from #t where num between 11 and 20

 

(2) sp_cursoropen
利用游标,性能较差,原理类似在程序中把数据全部加载到本地,然后通过游标移动来分页。不作推荐。

 

相关实践学习
使用SQL语句管理索引
本次实验主要介绍如何在RDS-SQLServer数据库中,使用SQL语句管理索引。
SQL Server on Linux入门教程
SQL Server数据库一直只提供Windows下的版本。2016年微软宣布推出可运行在Linux系统下的SQL Server数据库,该版本目前还是早期预览版本。本课程主要介绍SQLServer On Linux的基本知识。 相关的阿里云产品:云数据库RDS&nbsp;SQL Server版 RDS SQL Server不仅拥有高可用架构和任意时间点的数据恢复功能,强力支撑各种企业应用,同时也包含了微软的License费用,减少额外支出。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/sqlserver
目录
相关文章
|
1月前
|
前端开发
bladex中自定义分页的写法~
bladex中自定义分页的写法~
16 0
|
3月前
PageHelper分页插件拼接动态排序语句
PageHelper分页插件拼接动态排序语句
|
9月前
|
SQL Oracle 关系型数据库
什么是分页?如何使用分页?(一)
什么是分页?如何使用分页?
114 0
|
8月前
|
存储 自然语言处理 JavaScript
js根据数据关键字实现模糊查询功能
js根据数据关键字实现模糊查询功能
351 0
|
9月前
Vue3手写分页在分页的基础上用到Pagination 分页组件
Vue3手写分页在分页的基础上用到Pagination 分页组件
52 0
|
9月前
|
SQL 存储 关系型数据库
什么是分页?如何使用分页?(二)
什么是分页?如何使用分页?
52 0
|
XML SQL Java
mybatis学习(23):分页1 多参数传递(索引方式)
mybatis学习(23):分页1 多参数传递(索引方式)
125 0
mybatis学习(23):分页1 多参数传递(索引方式)
|
前端开发
分页重复问题思考
目前项目中存在一个问题,列表会出现数据重复! 原因很容易想到,由于排序原因新添加数据会排在顶部。 勤劳的我们又要开始摸头了 :-)
195 0
sqlsever2008 简单分页查询例子
工资从高到低排序 输出工资是第4到6行数据 select top 3 * from emp where EMPNO not in(select top 3 EMPNO from emp order by sal des...
1126 0

热门文章

最新文章