先回答风的问题
这个存储过程还有好处的
我们知道分页存储过程一般会采用一个字段作为分页的排除字段,一个或多个字段作为排序字段
目前网上很多分页存储过程会范一个毛病,就是排除字段和排序字段作为同一个参数传进来,这样就引发几个矛盾
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
写的不太好,大家一起完善吧。可以根据情况减的
楼主,咱第一个问题就不讨论?
[[it] 本帖最后由 淡漠的茶 于 2008-4-5 18:58 编辑 [/it]]
这个存储过程还有好处的
我们知道分页存储过程一般会采用一个字段作为分页的排除字段,一个或多个字段作为排序字段
目前网上很多分页存储过程会范一个毛病,就是排除字段和排序字段作为同一个参数传进来,这样就引发几个矛盾
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
写的不太好,大家一起完善吧。可以根据情况减的
楼主,咱第一个问题就不讨论?
[[it] 本帖最后由 淡漠的茶 于 2008-4-5 18:58 编辑 [/it]]