`
yesjavame
  • 浏览: 657536 次
  • 性别: Icon_minigender_2
  • 来自: 杭州
文章分类
社区版块
存档分类
最新评论

老生常谈:也来讨论一下SQL分页的问题

阅读更多

虽然说这个话题真的是外婆级的话题,但却是在我们的开发中经常遇到的问题。一般,我们会抛弃在RecordSet中分页的做法,因为显而易见的原因,这种方法的效率是非常低的。于是SQL分页是一个唯一的选择。

我一直是使用下面这种分页语句:

SELECT*FROM(SELECTTOP页大小*FROM(SELECTTOP起始记录+页大小*FROMORDERBY排序字段DESC)t1ORDERBY排序字段)t2ORDERBY排序字段DESC

这种分页方法通用、效率也不低,但有一个致命的问题,就在于最后一页的处理上。如果最后一页不足额定的每页显示条数(页大小),则会从前一页拉一些记录来凑数。

最近我又从网上找了几种流行的SQL分页,综合考虑效率与通用性,我测试了以下两种:

方案一:利用Not In和SELECT TOP分页

SELECTTOP页大小*FROMWHERE(主键NOTIN(SELECTTOP起始记录主键FROMORDERBY排序字段))ORDERBY排序字段

方案二:利用ID大于多少和SELECT TOP分页

SELECTTOP页大小*FROMWHERE(排序字段>(SELECTMAX(排序字段)FROM(SELECTTOP起始记录排序字段FROMORDERBY排序字段)AST))ORDERBY排序字段

加上我常用的那个:利用SELECT TOP来回倒分页,算是方案三吧。一起做了一个简单测试。

先创建表t_hello,很简单的一个表:

CREATETABLE[t_hello](
[sysid][uniqueidentifier]NOTNULLCONSTRAINT[DF_t_hello_sysid]DEFAULT(newid()),
[cdate][datetime]NOTNULLCONSTRAINT[DF_t_hello_cdate]DEFAULT(getdate()),
[title][nvarchar](50)COLLATEChinese_PRC_CI_ASNULL,
CONSTRAINT[PK_t_hello]PRIMARYKEYCLUSTERED
(
[sysid]
)
ON[PRIMARY]
)
ON[PRIMARY]
GO

然后插入100万条记录,呵呵,有点变态。就可以测试了,写段代码:

SqlConnectionconnection=null;

try
{
connection
=newSqlConnection(conStr);
connection.Open();

SqlCommandcmd
=newSqlCommand();
cmd.Connection
=connection;
cmd.CommandTimeout
=connection.ConnectionTimeout;

foreach(intstartinstarts)
{
cmd.CommandText
="相应的SQL语句";
SqlDataReaderreader
=cmd.ExecuteReader();
reader.Close();
}

connection.Close();
}

catch(Exceptionex)
{
if(connection!=null)connection.Close();
}

其中的starts 是一个int数组,里面有 {10,500,2000,10000,500000}。分别套用三个不同的SQL语句,结果如下:

方案1
开始:13:08:02.408145
结束:13:08:19.793144
Test 1 持续时间:00:00:17.3849984

方案 2
开始:13:08:19.803158
结束:13:08:20.033489
Test 2 持续时间:00:00:00.2303312

方案3
开始:13:08:20.043504
结束:13:08:24.449840
Test 3 持续时间:00:00:04.4063360


大家也可以参考squirrel_sc做的测试,http://www.cnblogs.com/squirrel_sc/archive/2004/10/02/48583.html以及http://blog.csdn.net/lihonggen0/archive/2004/09/14/103511.aspx

第二种方案的效率真是太明显了(当然如果cdate没有建立索引,它的效率也非常低)。第一方案Not In的方法显然不太可能成为我们的选择。我是看上了方案二的如此高的效率,但进一步研究却发现这个SQL语句在使用中还有不少要注意的地方。

首先,这个MAX(id)对id这个字段是有要求的,GUID就不能用了,这不算太什么,反正我也不会用guid来排序。一般排序的字段也就是日期、名称之类的。

其次,我常用的按日期降序排列(降序应该是最常用的排法了吧?),那这句话就变成了:

SELECTTOP'+cast(@sizeasnvarchar(8))+'*
FROMt_hello
WHERE(
cdate
<(SELECTMIN(cdate)FROM(SELECTTOP'+cast(@startasnvarchar(8))+'cdateFROMt_helloORDERBYcdatedesc)AST)
)
ORDERBYcdatedesc

除了要加上DESC以外,还有两处变化(一是把大小变成小于,二是把MAX变成MIN)。

第三,上面的句子还有点问题,从网上看到 的原始语句中"起始记录(start)"是"页数*页大小"。这里的页数有问题,能不能取0呢?如果取0,TOP语句就会出错,如果不取0,1*页大小, 那第一页的内容就会丢掉。(大家可以试一下,比如1*20)用起始记录也一样,从1开始还是从0开始,所以需要把小于号变成“小于等于”,同理,大于号也 要变成“大于等于”。然后,不用页数*页大小,改成“起始记录”,就解决问题了。

总得来说,方案二的确是一个不错的方法。:)

分享到:
评论

相关推荐

    浅谈SQLServer数据库分页

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

    浅谈SQL Server数据库分页

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

    MySQL百万级数据量分页查询方法及其优化建议

    数据库SQL优化是老生常谈的问题,在面对百万级数据量的分页查询,又有什么好的优化建议呢?下面将列举了一些常用的方法,供大家参考学习! 方法1: 直接使用数据库提供的SQL语句 语句样式: MySQL中,可用如下方法: ...

    我的WafBypass之路(SQL注入篇)

    去年到现在就一直有人希望我出一篇关于waf绕过的文章,我觉得这种老生常谈的话题也没什么可写的。很多人一遇到waf就发懵,不知如何是好,能搜到的各种姿势也是然并卵。但是积累姿势的过程也是迭代的,那么就有了此文...

    SQL Server使用游标处理Tempdb究极竞争-DBA问题-程序员必知

    SQL Server tempdb分配竞争算是DBA老生常谈的问题了,几乎现在所有的DBA都知道多建几个文件来解决/缓解问题.但是深层次的的竞争依旧不可避免.这里给大家剖析下游标在tempdb中的特点使其在一定场景下替代临时表/表变量...

    老生常谈外链 站长要做到对症下药.pps

    老生常谈外链 站长要做到对症下药.pps

    IE6实现position:fixed bug (固定窗口方法)的实例

    这个内容是老生常谈了,主要问题就是IE6不支持 position:fixed 引起的BUG.当我们去搜索解决这个bug的垮浏览器解决办法时,绝大多数结果都是说使用 position:absolute 来替代解决,可是我们真的解决了么?没有,因为当页面...

    简单实现SQLServer转MYSQL的方法

    SqlServer数据转换成mysql数据,可以说是一个老生常谈了,网上也有很多的方法,今天我们来看一种不一样的方法,而且也非常的简单,虽然有点小缺陷,但还是不失为一种很好的方法,当然如果结合mss2sql那就非常完美了

    老生常谈C++中实参形参的传递问题

    下面小编就为大家带来一篇老生常谈C++中实参形参的传递问题。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧

    老生常谈PHP中的数据结构:DS扩展

    下面小编就为大家带来一篇老生常谈PHP中的数据结构:DS扩展。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧

    SQL Server误区30日谈 第7天 一个实例多个镜像和日志传送延迟

    误区 #7:一个数据库可以存在多个镜像 错误 这个误区就有点老生常谈了。每一个主体服务器只允许一个镜像服务器。如果你希望存在多个主体服务器的副本,那么请使用事务日志传送,事务日志传送允许针对每一个主体存在多...

    老生常谈Log4j和Log4j2的区别(推荐)

    下面小编就为大家带来老生常谈Log4j和Log4j2的区别(推荐)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧

    老生常谈遮罩层 滚动条的问题

    今天遇到的问题是,在弹出层后面的 遮罩层,因为有滚动条,导致滚动条下面不可视区域没有遮罩层,解决方式是加的css。 js代码 [removed] //显示灰色JS遮罩层 function showBg(ct,content){ var bH=$(document)....

    构建高效的python requests长连接池详解

    老生常谈: python下的httpclient库哪个最好用? 我想大多数人还是会选择requests库的。原因么?也就是简单,易用! 如何蛋疼的构建reqeusts的短连接请求: python requests库默认就是长连接的 (http 1.1, Connection...

    老生常谈JavaScript面向对象基础与this指向问题

    在学习JavaScript的过程中,OOP是非常重要的一环,下面我们来一起探讨一下JS中的面向对象吧!!! 1 、OOP的基础问题 1.1什么是面向过程和面向对象? 面向过程:专注于如何去解决一个问题的过程步骤。编程特点是由...

    Java中文问题及最优解决方法

    由于Java编程中的中文问题是一个老生常谈的问题,在阅读了许多关于Java中文问题解决方法之后,结合作者的编程实践,我发现过去谈的许多方法都不能清晰地说明问题及解决问题,尤其是跨平台时的中文问题。于是我给出此...

    老生常谈计算机中的编码问题(必看篇)

    下面小编就为大家带来一篇老生常谈计算机中的编码问题(必看篇)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧

Global site tag (gtag.js) - Google Analytics