[ MSSQL ]分页排序存储过程

第一种方案,我们先取集集合OB,这可以用一个使用SELECT TOP 并使用升级排列的语句完成

然后对OB结果集进行倒序排序,再用"SELECT TOP 每页记录数"倒序 的方式取得目标集合AB,下面是存储过程

 

 
  
create PROCEDURE dbo.GetPagingData

(

@tablename varchar ( 100 ), -- 表名或视图表

@fieldlist varchar ( 4000 ) = ' * ' , -- 欲选择字段列表

@orderfield varchar ( 100 ), -- 排序字段

@keyfield varchar ( 100 ), -- 主键

@pageindex int , -- 页号,从0开始

@pagesize int = 20 , -- 页尺寸

@strwhere varchar ( 4000 ), -- 条件

@ordertype bit = 1 -- 排序,1,降序,0,升序

)

AS

BEGIN

SET NOCOUNT ON

declare @sqlstr varchar ( 6000 )

-- 处理SQL中危险字符,并且将条件处理成易嵌入的形式

set @sqlstr = ' declare @Rcount int; '

set @sqlstr = @sqlstr + ' set @rcount=(select count( ' + @keyfield + ' ) from ' + @tablename + ' where ' + @strWhere + ' ); '

set @strwhere = replace ( @strwhere , '''' , '''''' )

set @strwhere = replace ( @strwhere , ' -- ' , '' )

set @strwhere = replace ( @strwhere , ' ; ' , '' )

set @sqlstr = @sqlstr + ' declare @Rnum int; '

set @sqlstr = @sqlstr + ' set @rnum=@rcount- ' + cast ( @pagesize as varchar ) + ' * ' + cast ( @pageindex as varchar ) + ' ; '

set @sqlstr = @sqlstr + ' declare @sqlstr varchar(6000); '

if @ordertype = 1

begin

set @sqlstr = @sqlstr + ' set @sqlstr= '' select top ' + cast ( @Pagesize as varchar ) + ' ' + @fieldlist + ' from (select top 100

percent * from (select top
'' +cast(@rnum as varchar)+ '' * from ' + @tablename + ' where ' + @strwhere + '

order by
' + @orderfield + ' asc) as b order by paymoney desc) as a order by ' + @orderfield + ' desc '' ; '

end

else

begin

set @sqlstr = @sqlstr + ' set @sqlstr= '' select top ' + cast ( @Pagesize as varchar ) + ' ' + @fieldlist + ' from (select top 100

percent * from (select top
'' +cast(@rnum as varchar)+ '' * from ' + @tablename + ' where ' + @strwhere + '

order by
' + @orderfield + ' desc) as b order by paymoney asc) as a order by ' + @orderfield + ' asc '' ; '

end

set @sqlstr = @sqlstr + ' if @Rcount>0 begin execute(@sqlstr) end '

-- print @sqlstr

execute ( @sqlstr )

END

 

 

在上面的代码中,还处理了没有符合条件结果的情况

第二种方案的思想是这样的,先用倒序的SELECT TOP (页序号+1)*页尺寸的方法取得AE结果集,再从AE结果集中用NOT IN 的方法排除掉用SELECT TOP 页序号*页尺寸的方法取得的结果集,最后,对目标结果集执行倒序,下面是实现的代码

 

ContractedBlock.gif ExpandedBlockStart.gif View Code
 
   
create PROCEDURE dbo.GetPagingRecord

(

@tablename varchar ( 100 ), -- 表名或视图表

@fieldlist varchar ( 4000 ) = ' * ' , -- 欲选择字段列表

@orderfield varchar ( 100 ), -- 排序字段

@keyfield varchar ( 100 ), -- 主键

@pageindex int , -- 页号,从0开始

@pagesize int = 20 , -- 页尺寸

@strwhere varchar ( 4000 ), -- 条件

@ordertype bit = 1 -- 排序,1,降序,0,升序

)

AS

SET NOCOUNT ON

declare @sqlstr varchar ( 6000 )

-- 处理SQL中危险字符,并且将条件处理成易嵌入的形式

set @strwhere = replace ( @strwhere , '''' , '''''' )

set @strwhere = replace ( @strwhere , ' -- ' , '' )

set @strwhere = replace ( @strwhere , ' ; ' , '' )

set @sqlstr = ' declare @CurPageNum int; '

set @sqlstr = @sqlstr + ' declare @nextpagenum int; '

set @sqlstr = @sqlstr + ' set @curpagenum= ' + cast ( @PageIndex as varchar ) + ' * ' + cast ( @Pagesize as varchar ) + ' ; '

set @sqlstr = @sqlstr + ' set @nextpagenum= ' + cast ( @PageIndex + 1 as varchar ) + ' * ' + cast ( @Pagesize as varchar ) + ' ; '

set @sqlstr = @sqlstr + ' declare @sqlstr varchar(6000); '

if @ordertype = 1

begin

set @sqlstr = @sqlstr + ' set @sqlstr= '' select ' + @fieldlist + ' from ( select top '' +cast(@nextpagenum as varchar)+ '' * from

' + @tablename + ' where ' + @strwhere + ' order by ' + @orderfield + ' desc ) as a where ' + @keyfield + ' not in (

select top
'' +cast(@curpagenum as varchar)+ '' ' + @keyfield + ' from ' + @tablename + ' where ' + @strwhere + '

order by
' + @orderfield + ' desc) order by ' + @orderfield + ' desc '' ; '

end

else

begin

set @sqlstr = @sqlstr + ' set @sqlstr= '' select ' + @fieldlist + ' from ( select top '' +cast(@nextpagenum as varchar)+ '' * from

' + @tablename + ' where ' + @strwhere + ' order by ' + @orderfield + ' asc ) as a where ' + @keyfield + ' not in (

select top
'' +cast(@curpagenum as varchar)+ '' ' + @keyfield + ' from ' + @tablename + ' where ' + @strwhere + '

order by
' + @orderfield + ' asc) order by ' + @orderfield + ' asc '' ; '

end

set @sqlstr = @sqlstr + ' execute( @sqlstr) '

-- print @sqlstr

execute ( @sqlstr )

 

需要注意的是,如果要避免SQL注入式攻击,请注意处理像分号,双减号,单引号这些在SQL中有特殊含义的字符

至于上面两个存储过程哪个性能更好,那就取决于是对一个倒序的结果集再进入一次反序排列好呢,还是用NOT IN从一个大的结果集移除一个小的结果集好

第三种方案

成熟的分类过程

ContractedBlock.gif ExpandedBlockStart.gif View Code
 
   
create PROCEDURE CMS_SearchGetDataByPage
@tblName varchar ( 255 ), -- 表名
@fldNames varchar ( 1000 ), -- 选择的字段列表以,分隔
@selectOrderFldName varchar ( 500 ), -- 排序字段以,分隔(不能含keyFldName指定的字段,可为空)
@orderFldDesc varchar ( 500 ), -- 排序字段及排序方向,如addDate desc,id desc(排序字段需通过selectOrderFldName指定,可为空)
@keyFldName varchar ( 255 ), -- 主键字段
@PageSize int = 10 , -- 页尺寸
@PageIndex int = 1 , -- 页码
@strWhere varchar ( 1000 ) = '' -- 查询条件(注意: 不要加where)
AS
declare @strWhereA varchar ( 1200 ) -- 临时变量,给sqlwhere加where
declare @strOrderA varchar ( 2000 ) -- 第一次排序类型
declare @strOrderB varchar ( 2000 ) -- 第二次排序类型
declare @strSqlA varchar ( 4000 ) -- 第一次选出
declare @strSqlB varchar ( 8000 ) -- 第二次选出
declare @strSQL varchar ( 8000 ) -- 最后选出
/*
条件 */
if @strWhere != ''
set @strWhereA = ' where ' + @strWhere
else
set @strWhereA = ''

/* 选择字段列表 */
if @fldNames is null or rtrim ( @fldNames ) = ''
set @fldNames = ' * '

/* 排序字段列表 */
if not ( @selectOrderFldName is null or rtrim ( @selectOrderFldName ) = '' )
if rtrim ( @selectOrderFldName ) = ' id '
set @selectOrderFldName = ''
else
set @selectOrderFldName = ' , ' + @selectOrderFldName

/* 构建order,按指定方式排序 */
if @orderFldDesc is null or rtrim ( @orderFldDesc ) = ''
set @orderFldDesc = ' order by id desc '
else
set @orderFldDesc = ' order by ' + @orderFldDesc
set @strOrderA = UPPER ( @orderFldDesc )
set @strOrderB = replace ( @strOrderA , ' DESC ' , ' DESC1 ' )
set @strOrderB = replace ( @strOrderB , ' ASC ' , ' DESC ' )
set @strOrderB = replace ( @strOrderB , ' DESC1 ' , ' ASC ' )

/* 第一页 */
if @PageIndex = 1
set @strSQL = ' select top ' + str ( @PageSize ) + ' ' + @fldNames + ' from [ ' + @tblName + ' ] with(nolock) ' + @strWhereA + ' ' + @strOrderA
else
begin
-- 取得总记录数
declare @sql nvarchar ( 500 )
declare @maxCount int
declare @maxPage int
declare @tempRowCount int
set @sql = ' select @maxCount = count( ' + @keyFldName + ' ) from [ ' + @tblName + ' ] ' + @strWhereA
exec sp_executesql @sql ,N ' @maxCount int output ' , @maxCount output
set @maxPage = @maxCount / @PageSize
if ( @maxCount % @PageSize > 0 )
set @maxPage = @maxPage + 1
/* 最后一页 */
if @PageIndex >= @maxPage
begin
set @PageIndex = @maxPage
set @strSqlA = char ( 13 ) + ' (select top ' + str ( @maxCount % @PageSize ) + ' ' + @keyFldName + @selectOrderFldName + ' from [ ' + @tblName + ' ] as a with(nolock) ' + @strWhereA + @strOrderB + ' ) ' + char ( 13 )
set @strSqlB = char ( 13 ) + ' (select ' + @keyFldName + ' from ' + @strSqlA + ' as b ) ' + char ( 13 )
set @strSQL = ' select ' + @fldNames + ' from [ ' + @tblName + ' ] where ([ ' + @keyFldName + ' ] in ' + @strSqlB + ' ) ' + @strOrderA + char ( 13 )
end
else
begin
/* 不是第一页,也不是最后一页 */
if ( @PageIndex <= @maxPage / 2 )
begin
-- 前半数的页
set @tempRowCount = @PageIndex * @PageSize
/* 构建SQL,本分页算法的目的是为了实现高效的非主键排序的分页。by tony */
/* 1、先按指定字段+主键字段按降序选出perPage*pageNum条记录 */
set @strSqlA = char ( 13 ) + ' (select top ' + str ( @tempRowCount ) + ' ' + @keyFldName + @selectOrderFldName + ' from [ ' + @tblName + ' ] as a with(nolock) ' + @strWhereA + @strOrderA + ' ) ' + char ( 13 )
/* 2、再从选出的记录中按升序选出perPage条记录 */
set @strSqlB = char ( 13 ) + ' (select top ' + str ( @PageSize ) + ' ' + @keyFldName + ' from ' + @strSqlA + ' as b ' + @strOrderB + ' ) ' + char ( 13 )
/* 3、从数据库中选出主键在第二次选出的记录中的记录,按降序排列,分页完成 */
set @strSQL = ' select ' + @fldNames + ' from [ ' + @tblName + ' ] where ([ ' + @keyFldName + ' ] in ' + @strSqlB + ' ) ' + @strOrderA
end
else
begin
-- 后半数的页
set @tempRowCount = @maxCount - ( @PageIndex - 1 ) * @PageSize
/* 构建SQL,本分页算法的目的是为了实现高效的非主键排序的分页。by tony */
/* 1、先按指定字段+主键字段按降序选出perPage*pageNum条记录 */
set @strSqlA = char ( 13 ) + ' (select top ' + str ( @tempRowCount ) + ' ' + @keyFldName + @selectOrderFldName + ' from [ ' + @tblName + ' ] as a with(nolock) ' + @strWhereA + @strOrderB + ' ) ' + char ( 13 )
/* 2、再从选出的记录中按升序选出perPage条记录 */
set @strSqlB = char ( 13 ) + ' (select top ' + str ( @PageSize ) + ' ' + @keyFldName + ' from ' + @strSqlA + ' as b ' + @strOrderA + ' ) ' + char ( 13 )
/* 3、从数据库中选出主键在第二次选出的记录中的记录,按降序排列,分页完成 */
set @strSQL = ' select ' + @fldNames + ' from [ ' + @tblName + ' ] where ([ ' + @keyFldName + ' ] in ' + @strSqlB + ' ) ' + @strOrderA
end
end
end

set nocount on

/* print @strSQL */ -- 显示SQL
exec ( @strSQL )
set nocount off
RETURN

第四种方案

ContractedBlock.gif ExpandedBlockStart.gif View Code
 
   
CREATE PROC SP_PageList
@tbname sysname, -- 要分页显示的表名
@FieldKey sysname, -- 用于定位记录的主键(惟一键)字段,只能是单个字段
@PageCurrent int = 1 , -- 要显示的页码
@PageSize int = 10 , -- 每页的大小(记录数)
@FieldShow nvarchar ( 1000 ) = '' , -- 以逗号分隔的要显示的字段列表,如果不指定,则显示所有字段
@FieldOrder nvarchar ( 1000 ) = '' , -- 以逗号分隔的排序字段列表,可以指定在字段后面指定DESC/ASC
-- 用于指定排序顺序
@Where nvarchar ( 1000 ) = '' , -- 查询条件
@RecordCount int OUTPUT, -- 总记录数
@PageCount int OUTPUT -- 总页数
AS
DECLARE @sql nvarchar ( 4000 )
SET NOCOUNT ON
-- 检查对象是否有效
IF OBJECT_ID ( @tbname ) IS NULL
BEGIN
RAISERROR (N ' 对象"%s"不存在 ' , 1 , 16 , @tbname )
RETURN
END
IF OBJECTPROPERTY ( OBJECT_ID ( @tbname ),N ' IsTable ' ) = 0
AND OBJECTPROPERTY ( OBJECT_ID ( @tbname ),N ' IsView ' ) = 0
AND OBJECTPROPERTY ( OBJECT_ID ( @tbname ),N ' IsTableFunction ' ) = 0
BEGIN
RAISERROR (N ' "%s"不是表、视图或者表值函数 ' , 1 , 16 , @tbname )
RETURN
END

-- 分页字段检查
IF ISNULL ( @FieldKey ,N '' ) = ''
BEGIN
RAISERROR (N ' 分页处理需要主键(或者惟一键) ' , 1 , 16 )
RETURN
END

-- 其他参数检查及规范
IF ISNULL ( @PageCurrent , 0 ) < 1 SET @PageCurrent = 1
IF ISNULL ( @PageSize , 0 ) < 1 SET @PageSize = 10
IF ISNULL ( @FieldShow ,N '' ) = N '' SET @FieldShow = N ' * '
IF ISNULL ( @FieldOrder ,N '' ) = N ''
SET @FieldOrder = N ''
ELSE
SET @FieldOrder = N ' ORDER BY ' + LTRIM ( @FieldOrder )
IF ISNULL ( @Where ,N '' ) = N ''
SET @Where = N ''
ELSE
SET @Where = N ' WHERE ( ' + @Where + N ' ) '

-- 如果@PageCount为NULL值,则计算总页数(这样设计可以只在第一次计算总页数,以后调用时,把总页数传回给存储过程,避免再次计算总页数,对于不想计算总页数的处理而言,可以给@PageCount赋值)
IF @PageCount IS NULL
BEGIN
SET @sql = N ' SELECT @PageCount=COUNT(*) '
+ N ' FROM ' + @tbname
+ N ' ' + @Where
EXEC sp_executesql @sql ,N ' @PageCount int OUTPUT ' , @PageCount OUTPUT
SET @RecordCount = @PageCount
SET @PageCount = ( @PageCount + @PageSize - 1 ) / @PageSize
END

-- 计算分页显示的TOPN值
DECLARE @TopN varchar ( 20 ), @TopN1 varchar ( 20 )
SELECT @TopN = @PageSize ,
@TopN1 = @PageCurrent * @PageSize

-- 第一页直接显示
IF @PageCurrent = 1
EXEC (N ' SELECT TOP ' + @TopN
+ N ' ' + @FieldShow
+ N ' FROM ' + @tbname
+ N ' ' + @Where
+ N ' ' + @FieldOrder )
ELSE
BEGIN
SELECT @PageCurrent = @TopN1 ,
@sql = N ' SELECT @n=@n-1,@s=CASE WHEN @n< ' + @TopN
+ N ' THEN @s+N '' , '' +QUOTENAME(RTRIM(CAST( ' + @FieldKey
+ N ' as varchar(8000))),N '''''''' ) ELSE N '''' END FROM ' + @tbname
+ N ' ' + @Where
+ N ' ' + @FieldOrder
SET ROWCOUNT @PageCurrent
EXEC sp_executesql @sql ,
N
' @n int,@s nvarchar(4000) OUTPUT ' ,
@PageCurrent , @sql OUTPUT
SET ROWCOUNT 0
IF @sql = N ''
EXEC (N ' SELECT TOP 0 '
+ N ' ' + @FieldShow
+ N ' FROM ' + @tbname )
ELSE
BEGIN
SET @sql = STUFF ( @sql , 1 , 1 ,N '' )
-- 执行查询
EXEC (N ' SELECT TOP ' + @TopN
+ N ' ' + @FieldShow
+ N ' FROM ' + @tbname
+ N ' WHERE ' + @FieldKey
+ N ' IN( ' + @sql
+ N ' ) ' + @FieldOrder )
END
END
GO

转载于:https://www.cnblogs.com/samsonhuang/articles/2092839.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值