编程中国 | 业界新闻 | 技术文章 | 视频教程 | 下载频道 | 程序源码 | 个人空间 | 编程论坛  
全能 ASP / PHP / ASP.NET 主机,支持月付专业 MSSQL 数据库空间,支持月付专业 MySQL 数据库空间,支持月付学习型 ASP/PHP/ASP.NET 主机 30元/年
发新话题
打印

存储过程自定义分页详解

本主题由 静夜思 于 2008-4-5 10:56 鉴定为原创并加为精华
先回答风的问题
这个存储过程还有好处的
我们知道分页存储过程一般会采用一个字段作为分页的排除字段,一个或多个字段作为排序字段
目前网上很多分页存储过程会范一个毛病,就是排除字段和排序字段作为同一个参数传进来,这样就引发几个矛盾
1。由于排除字段的局限,无法多个字段排序
2。当这个字段为非主键时,就会发现一个问题,非主键代表不强制表内唯一,这就代表此字段可能有相同值,如果使用not in,就会把多条相同值排除掉,假如第一页中某一行和第二页中某一行上这个字段值相同,比如一个标记类别的非主键字段,都为数码相机,那么我们将第一页作为条件,使用not in来排除的话,那么第二页这行也会被排除,因为not in ,就是不等于这个序列中任何一个,而且后面都会被排除,这就造成丢失
3.当这个字段为非主键时,如果采用> < between and这样的排除方法时,也可能产生丢失,如果是连续几行这个字段值相同,就可能出现多取几行或者少取几行这样的不确定性,比如如果第二页10行,最后三行此字段相同,那么就会有7行 8行 ,如果第三页第一行还相同,那就可能第二页取到11行,这取决与你的表达式,总之是没有一个确切表达式

一般解决办法有三个,
第一种就是lz所用方法了,就是排除与排序参数分离,利用主键唯一性,使用not in将这页前的字段值全排除。这样通过重新排序和top选择,就会得到这页数据了,这是解决的方法之一,具备很大的通用性,对于中小型应用还是合适的
但是这样方法不是没有缺点的,就是在百万数据这个级别上 not in过于缓慢,一个原因是not in对索引不起作用(这点我不太确定,我只是听以前我们经理说的,呵呵),第二个原因由于sql执行顺序问题,导致必然的两次全表搜索,百万级数据上全表检索是很恐怖的,第三个原因not in所用的排除序列,在前面有百万条数据时,这个序列是很长的,可能有百万个值用逗号分隔,这个可能光内存就耗不少吧

第二种方法是查询时加入一个临时排序字段,用的不多

第三种方法是分离条件,将主键做一条件,非主键做一条件,或者根据需要就不利用非主键,再某些简单情况下
刚才我合了一个,写的不完全,大家补下吧

CREATE PROCEDURE List
@strFrom varchar(50) ,   --表名
@strFields varchar(200) = '*',  --查询列名
@strWhere  varchar(300) = '',     --查询条件
@strOrder varchar(100) = '', --排序列名
@pageIndex int = 1 ,         --页码
@pageSize int = 15,         --页面大小
@intOrder int = 0,        --排序类型  1为升序
@pk varchar(200)='',     --not in情况下给这个主键值
@countAll bigint output              --返回纪录总数用于计算页面数  
AS
begin
--声明开始
declare   @countSQL   nvarchar(500) --记数语句
declare @strTemp varchar(100) --临时变量
declare @strOrders varchar(50) --排序语句
declare @strSQL varchar(1000)--主语句
declare   @R bigint
--声明结束

--统计记录总数开始
if @strWhere=''
    begin
        set @countSQL=N'select @R=count(*) from '+convert(nvarchar(50),@strFrom)
    end
else
    begin
        set @countSQL=N'select @R=count(*) from '+convert(nvarchar(50),@strFrom)+' where '+convert(nvarchar(300),@strWhere)
    end
exec SP_EXECUTESQL @countSQL, N' @R BIGINT OUTPUT',  @R OUTPUT
set   @countAll=  @R   
--统计记录总数结束

--排序开始
if @intOrder=0
    begin
        set @strTemp = '<(select min'
        set @strOrders = 'order by  '+@strOrder+' desc '
    end
else
   begin
       set @strTemp = '>(select max'
       set @strOrders = 'order by  '+@strOrder+' asc '
   end
--排序结束

--获取主语句开始
if @pageIndex=1
    begin
        if @strWhere=''
        begin
            set @strSQL='select top '+str(@pageSize)+'  '+@strFields+' from '+@strFrom+' '+@strOrders
        end
        else
        begin
            set @strSQL='select top '+str(@pageSize)+'  '+@strFields+' from '+@strFrom+' where '+@strWhere+' '+@strOrders
        end
    end
else
    begin
        if @pk=''
        begin
        if @strWhere =''
        begin
             set @strSQL= 'select top '+str(@pageSize)+' '+@strFields+' from '+@strFrom+' where '+@strOrder+@strTemp+' ('+@strOrder+')' +' from (select top '+str((@pageIndex-1)*@pageSize)+' '+@strFields+' from '+@strFrom+ ' '+@strOrders+ ') as tempTable ) '+@strOrders
        end
        else
        begin
             set @strSQL= 'select top '+str(@pageSize)+' '+@strFields+' from '+@strFrom+' where '+@strOrder+@strTemp+' ('+@strOrder+')' +' from (select top '+str((@pageIndex-1)*@pageSize)+' '+@strFields+' from '+@strFrom+ ' where '+@strWhere+' '+@strOrders+ ') as tempTable ) AND '+@strWhere+' '+@strOrders
        end
        end
        else
        begin
            if @strWhere=''
            begin
                set @strSQL= 'select top '+str(@pageSize)+' '+@strFields+' from '+@strFrom+' where '+@pk+' not in (select top '+str((@pageIndex-1)*@pageSize)+' '+@pk+' from '+@strFrom+ ' '+@strOrders+ ') '+@strOrders
            end
            else
            begin
                set @strSQL= 'select top '+str(@pageSize)+' '+@strFields+' from '+@strFrom+' where '+@pk+' not in (select top '+str((@pageIndex-1)*@pageSize)+' '+@pk+' from '+@strFrom+' where '+@strWhere+ ' '+@strOrders+ ') and '+@strWhere+' '+@strOrders
            end
        end
    end
--获取主语句结束

exec(@strSQL) --执行
end
GO

写的不太好,大家一起完善吧。可以根据情况减的
楼主,咱第一个问题就不讨论?

[ 本帖最后由 淡漠的茶 于 2008-4-5 18:58 编辑 ]

TOP

提示: 作者被禁止或删除 内容自动屏蔽

TOP

我先从功能上分析一下淡漠的茶的这段代码:
首先淡漠的茶的这段代码可以说比我的多考虑了一个情况.就是在没有主键的情况下,我们先给表排序,完了再开始进行……,其他的方面基本上就差不多了……
再者我从性能上说说:
如果将我和他谈论的第一个问题作为比较的条件的话,他的是比我的多出来了一个将第一页分离的情况。虽然他给出了没有主键的情况的代码,但是这个不能作为性能方面的比较,首先很少有表没有主键的吧,甚至可以说是没有吧,再者……(这个再者留到下一段说)
我们来对比核心的内容:要执行的sql语句……但是好像没有区别……他还是采取了我的那种思想,而且要执行的过程也是没有减少……
总结的说就是如果不将第一个问题做为比较的条件的话,这两个存储过程在性能方面就没有什么区别可言……
而分有主键和没有主键我认为说不上多余反正也用途不是太大,因为很少有表是没有主键的……
不过我还是要感谢淡漠的茶一起探讨

[ 本帖最后由 hebingbing 于 2008-4-5 20:18 编辑 ]
【www.bcsnjd.cn】编程少年基地提供在线视频教程、电子图书、程序源码等众多资源  近期将推出第六届齐鲁大学生软件设计大赛参赛队员及评委老师的寄语专题

TOP

精彩的交流,看了有收获,呵呵
我是爱喝茶的胖子,有空一起喝茶啊

TOP

精彩 大家加这个群 58808531
  里面都是我们这个论坛的朋友,希望大家都加入

TOP

怎么加了where不能用

@SelectWhere=''里面是要加where的吧,看了你的句子有2个句子前面没有写where.
还有where写了后还是出错,找不出什么原因.
你的加了where能用?

TOP

1111

TOP

佩服佩服!楼主写得好,淡漠的茶回帖更好!
www.tiray.net

TOP

<div class="notice" style="width: 500px">提示: <em>作者被禁止或删除 内容自动屏蔽</em></div>
好读书不求什解!

TOP

"<div class="notice" style="width: 500px">提示: <em>作者被禁止或删除 内容自动屏蔽</em></div>"
好读书不求什解!

TOP

发新话题