首页 新闻 论坛 群组 Blog 文档 下载 读书 Tag 网摘 搜索 .NET Java 游戏 视频 人才 外包 培训 数据库 书店 程序员
中国软件网
欢迎您:游客 | 登录 注册 帮助
  • 分页控件之分页算法 —— for SQL Server 版。百万级的数据只需要15毫秒到900毫秒 [已结贴,结贴人:jyk]
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • jyk
    • 等级:
    发表于:2008-05-07 19:39:33 楼主

    下载分页控件(包括源码和演示)

    分页控件使用方法

    关于分页的误区
        误区1:分页的时候,只有使用存储过程,效率才高。
        误区2:忽略了索引的作用。

    上两篇好像介绍的不太详细,这里详细说明一下分页控件里使用的分页算法,也就是SQL语句。

    分页一般分为四种情况

    1、单字段排序,排序字段没有重复值。
    2、单字段排序,排序字段有重复值。
    3、多字段排序,最后一个排序字段没有重复值。
    4、多字段排序,最后一个排序字段有重复值。

    其中第2、4 情况都可以再加一个排序字段(比如说主键),就可以转换成第三种情况。

    所以分页针对1、3两种情况设置了两种分页算法。

    1、单字段排序,排序字段没有重复值。

        公式:

    1declare @col int
    2
    3select top {PageSize * (PageIndex-1)+1} @col = [排序字段]
    4from [表名 ¦视图名]
    5[ where 查询条件 ]
    6order by [排序字段] asc ¦desc
    7
    8select top PageSize 需要显示的字段
    9from [表名 ¦视图名]
    10where [排序字段] >= @col
    11[ and 查询条件 ]
    12order by [排序字段] asc ¦desc
        以NorthWind 数据库里的 Products 表为例,假设一页显示10条数据,CategoryID = 3 为查询条件,按照ProductID 倒序,如果想显示第二页的数据,那么SQL语句就是
        declare @col int

    select top 11 @col = ProductID from Products where CategoryID = 3 order by ProductID desc   

    select top 10 * from Products where ProductID >= @col and CategoryID = 3 order by ProductID desc   
      说明:
        第一行的定义,要根据字段类型来修改,看是比较麻烦,但是这个麻烦交给分页控件就可以了,使用者,只要设置分页控件的属性就可以了。
        第五行和第十一行,如果需要加查询条件的话就可以在这里添加。

        第三行是一个“定位”,这个可以算是SQL Server 所特有的吧,也是SQL Server 很宽容的地方。以Products 表的例子,执行完第一条select 语句之后, @col 里面记录的是 在CategoryID = 3 的记录里面,按照ProductID 倒序,排行在11位的记录的值。
       
        第一个select 语句定位以后,第二个select 语句就可以根据这个“位置”继续向下查找数据了。

        虽然例子里面使用了ProductID(主键)来排序,但是并不是说这个算法只能用主键来排序,哪个字段都可以,但是要符合第一种情况,就是“只有一个排序字段,且排序字段里的记录没有重复值”!


    3、多字段排序,最后一个排序字段没有重复值。

        如果 Products 表想要用 UnitPrice 字段来排序怎么办呢?上面的算法是不适合的,我们需要使用另一种算法,这个和颠颠倒倒法有些类似,但是我做了一些优化。

        公式:
    select [需要显示的字段] from [表名 ¦视图名] where [主键字段] in

        ( select top PageSize [主键字段] from
            (select top {PageSize * PageIndex} [主键字段] , [排序字段] from    --有几个排序字段就写几个字段

                [表名 ¦视图名]
                [ where 查询条件 ]
                order by
               
                    [排序字段1] asc ¦desc ,
                    [排序字段2] desc ¦asc,
                   
                    [主键字段] asc ¦desc   
            ) as aa   
            order by   
                [排序字段1] desc ¦asc,        --如果上面是倒序,那么这里就是正序,下同
                    [排序字段2] asc ¦desc ,
                       
                        [主键字段] desc ¦asc

        )

    order by
        [排序字段1] asc ¦desc,    --如果上面是倒序,那么这里就是正序,所谓颠颠倒倒嘛。
        [排序字段2] desc ¦asc,
       
        [主键字段] asc ¦desc
        以NorthWind 数据库里的 Products 表为例,假设一页显示10条数据,CategoryID = 3 为查询条件,按照UnitPrice 倒序,由于UnitPrice 字段可能有重复值,所以加上一个排序字段——ProductID ,即按照 UnitPrice  desc,ProductID  来排序。 如果想显示第二页的数据,那么SQL语句就是
    select * from Products where ProductID in
        ( select top 10 ProductID from
            (select top 20 ProductID , UnitPrice from   
                Products
                where CategoryID = 3
                order by
                    UnitPrice desc ,
                    ProductID     
            ) as aa   
            order by   
                UnitPrice asc,        --如果上面是倒序,那么这里就是正序,下同
                    ProductID desc
        )
    order by
        UnitPrice desc,    --如果上面是倒序,那么这里就是正序,所谓颠颠倒倒嘛。
        ProductID
        说明:
        1、这里查询条件加一次就可以了。
        2、是不是看 asc ¦desc 倒来倒去的有点晕,恩,这就对了,颠颠倒倒嘛。
        3、最主要的就是第三个select 语句,他要取从第一条数据到要显示的页的数据,可见越是后面的记录,top n 就会越大,所以这里提取的数据就要做一个精简,只写排序需要的字段(主键字段和排序字段)。
        4、第二个select 语句是去掉前面不需要的页里的数据,只保留要显示的页号里的数据。
        5、第一个select 语句,用主键字段 in () 的方式提取其他需要的字段。
        6、这种分页算法有一个小的bug,就是显示最后一页数据的时候,会多出来几条记录,不过这个bug已经在分页控件里面修正了,最后一页的分页算法,采用特殊的select语句。
        7、效率,设置好索引,效率是没有问题的,上一篇随笔已经测试过了。
        8、这种算法有一个“侵入性”,就是要求表必须有主键,而且不能是联合主键,引为要用 in 的方式查询数据。但是并没有要求主键自身必须能够排序。

    测试效果
    记录数:2523136条。
    一页显示5条记录。


    //分页算法1 单字段排序,且排序字段是聚集索引。
      //1000 页以内 15毫秒
      //10000页以内 30毫秒
      //50000页以内 100多毫秒
      //100000页以内 200多毫秒
      //最后几页 第一次跳转到 4秒多
      //最后几页 连续向前翻页 1秒156毫秒

      //页号大范围跳转的时候需要的时间比较长,但是也小于1秒,同时SQL Server 占用的内存有所增加 120M。最后几页时达到320M

    ===================================================================
    以下是多排序字段的分页情况,排序字段是 UnitPrice,ProductID 

      //分页算法2 无索引  首页 8秒187毫秒 。
      //10 页以内 2秒812毫秒
      //速度太慢下面的就不测试了

      //分页2 非聚集索引 UnitPrice  首页 468毫秒
      //10 页以内 2秒671毫秒
      //速度太慢下面的就不测试了

     
      //分页算法2 非聚集索引 UnitPrice,ProductID  首页 500毫秒
      //10 页以内 2秒796毫秒
      //100页以内 4秒796毫秒
      //速度太慢下面的就不测试了

     
      //分页算法2 非聚集索引 UnitPrice,ProductID desc  首页 500毫秒
      //10 页以内 0-15毫秒
      //100页以内 15-46毫秒
      //1000页以内 31-62毫秒
      //10000页以内 100毫秒左右
      //50000页以内 400-500毫秒
      //100000页以内 900毫秒左右
      //最后几页 第一次跳转到 4秒421毫秒
      //最后几页 连续向前翻页 4秒375毫秒

      //页号大范围跳转的时候需要的时间比较长,但是也小于1秒,
      //这回SQL Server 占用的内存增加幅度不大 120M左右

    100  修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • xp1056
    • 等级:
    发表于:2008-05-07 20:22:361楼 得分:1
    学习
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • gui0605
    • 等级:
    发表于:2008-05-07 20:42:462楼 得分:1
    还没见过比吴旗娃那个更有效的
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • jyk
    • 等级:
    发表于:2008-05-07 20:44:533楼 得分:0
    自己测试一下,看看那个更有效。
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-07 20:49:334楼 得分:1
    学习!
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-07 20:53:085楼 得分:1
    不错,谢谢分享
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • jyk
    • 等级:
    发表于:2008-05-07 21:25:586楼 得分:0
    吴旗娃得分页控件生成的分页算法,按照 UnitPrice ,ProductID  排序,我在查询分析器里面测试

    declare @pagesize int
    declare  @pageindex int

    set @pagesize = 10

    set @pageindex = 3000

    declare  @docount bit

    set nocount on

    declare @indextable table(id int identity(1,1),nid int)
    declare @PageLowerBound int
    declare @PageUpperBound int
    set @PageLowerBound=(@pageindex-1)*@pagesize
    set @PageUpperBound=@PageLowerBound+@pagesize
    set rowcount @PageUpperBound

    insert into @indextable(nid) select ProductID from Products  where categoryid=3 order by UnitPrice ,ProductID

    select * from Products O,@indextable t where O.ProductID=t.nid
    and t.id between @PageLowerBound+1 and @PageUpperBound order by t.id

    显示第3页的数据,
    第一次查询需要的时间:6秒,
    再次查询:5秒。
    内存占用:116M


    显示第3000页的数据,
    第一次查询需要的时间:28秒,
    再次查询:5秒。
    内存占用:116M

    用了五秒的时间,实在是太慢了!

    即使是查可第三页也是花了5秒钟的时间,再次执行相同的语句还是五秒!

    =========

    select * from Products where ProductID in 
        ( select top 10 ProductID from 
            (select top 30000 ProductID , UnitPrice from     
                Products 
                where CategoryID = 3
                order by 
                    UnitPrice desc ,
                    ProductID     
            ) as aa     
            order by     
                UnitPrice asc,        --如果上面是倒序,那么这里就是正序,下同
                    ProductID desc 
        ) 
    order by 
        UnitPrice desc,    --如果上面是倒序,那么这里就是正序,所谓颠颠倒倒嘛。
        ProductID 

    显示第3页的数据,
    第一次查询需要的时间:10秒,
    再次查询:小于1秒。
    内存占用:62M


    显示第3000页的数据,
    第一次查询需要的时间:24秒,
    再次查询:2到3秒。
    内存占用:300M


    对比一下,在显示前几页的时候,综合优势还是比较明显的,只是在第一次查询的时候,多用了4秒的时间,再次执行的时候就几乎不需要时间了。
    3000页的时候,再次查询也是之需要2、3秒。

    吴旗娃的倒是很稳定,都是五秒的时间。
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-07 21:29:097楼 得分:1
    谢谢LZ
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • jyk
    • 等级:
    发表于:2008-05-07 21:35:228楼 得分:0
    select * from Products where ProductID in 
        ( select top 10 ProductID from 
            (select top 30000 ProductID , UnitPrice from     
                Products 
                --where CategoryID = 3
                order by 
                    UnitPrice desc ,
                    ProductID     
            ) as aa     
            order by     
                UnitPrice asc,        --如果上面是倒序,那么这里就是正序,下同
                    ProductID desc 
        ) 
    order by 
        UnitPrice desc,    --如果上面是倒序,那么这里就是正序,所谓颠颠倒倒嘛。
        ProductID 

    注意,没有加查询条件 --where CategoryID = 3
    这时速度就非常快,第一次查询和再次查询都小于1秒。

    内存占用 26M


    ===========

    declare @pagesize int
    declare  @pageindex int

    set @pagesize = 10

    set @pageindex = 3000

    declare  @docount bit

    set nocount on

    declare @indextable table(id int identity(1,1),nid int)
    declare @PageLowerBound int
    declare @PageUpperBound int
    set @PageLowerBound=(@pageindex-1)*@pagesize
    set @PageUpperBound=@PageLowerBound+@pagesize
    set rowcount @PageUpperBound

    insert into @indextable(nid) select ProductID from Products  order by UnitPrice desc,ProductID    --where CategoryID = 3

    select * from Products O,@indextable t where O.ProductID=t.nid
    and t.id between @PageLowerBound+1 and @PageUpperBound order by t.id


    而这个第一次查询还是需要 22秒。
    再次查询于1秒。
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • jyk
    • 等级:
    发表于:2008-05-07 21:40:349楼 得分:0
    declare @pagesize int
    declare  @pageindex int

    set @pagesize = 10

    set @pageindex = 3000

    declare  @docount bit

    set nocount on

    declare @indextable table(id int identity(1,1),nid int)
    declare @PageLowerBound int
    declare @PageUpperBound int
    set @PageLowerBound=(@pageindex-1)*@pagesize
    set @PageUpperBound=@PageLowerBound+@pagesize
    set rowcount @PageUpperBound

    insert into @indextable(nid) select ProductID from Products  order by ProductID    --where CategoryID = 3

    select * from Products O,@indextable t where O.ProductID=t.nid
    and t.id between @PageLowerBound+1 and @PageUpperBound order by t.id

    ========

        declare @col int

    select top 29991 @col = ProductID from Products  order by ProductID     

    select top 10 * from Products where ProductID >= @col  order by ProductID     


    这是单字段排序,都是小于1秒,谁快谁慢,只能靠感觉了。

    我说感觉我的快,可能你也不信,还是自己测试一下吧。

    另外Products 里的记录数是 2523136条。

    使用查询分析器得到的时间。
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • syc958
    • 等级:
    发表于:2008-05-07 22:57:5710楼 得分:1
    顶!做的不错!
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-08 00:49:5511楼 得分:1
    顶~
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-08 00:54:5812楼 得分:1
    留个记号,明天研究
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-08 01:02:4613楼 得分:1
    xue xi@
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • jyk
    • 等级:
    发表于:2008-05-08 06:45:3514楼 得分:0
    谢谢。
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • aaajedll
    • 等级:
    发表于:2008-05-08 08:06:0615楼 得分:1
    学习
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • clal
    • 等级:
    发表于:2008-05-08 08:10:0816楼 得分:1
    谢谢分享
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-08 08:36:5017楼 得分:1
    mark
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-08 08:41:4118楼 得分:1
    mark
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-08 08:51:4619楼 得分:1
    学习
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-08 15:44:1120楼 得分:1
    顶一个,学习
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-08 16:02:5621楼 得分:1
    靠,LZ的不错,深入学习中...
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-08 16:04:3722楼 得分:1
    mark!
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • suosa
    • 等级:
    发表于:2008-05-08 16:09:0323楼 得分:1
    学习
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • jyk
    • 等级:
    发表于:2008-05-08 19:14:2724楼 得分:0
    试了就知道了呀。
    不试不知道一试吓一跳,呵呵。
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-08 19:25:2125楼 得分:1
    xuexi
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-09 22:37:1226楼 得分:1
    谢谢分享 学习了..
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-10 09:16:4927楼 得分:0
    该回复于2008-05-11 11:49:47被版主删除
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • hy_lihuan
    • 等级:
    发表于:2008-05-10 09:27:5328楼 得分:1
    学习
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • ccaakkee
    • 等级:
    发表于:2008-05-10 09:33:1629楼 得分:1
    好 收藏
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-10 11:35:2730楼 得分:1
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-10 15:20:2231楼 得分:1
    谢谢,收藏了
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-10 15:27:4732楼 得分:0
    该回复于2008-05-10 15:36:24被版主删除
    修改 删除 举报 引用 回复