千万级分页高效存储过程(转)

http://www.chenjiliang.com/Article/View.aspx?ArticleID=13803

这两天测试了前几天写的SQL2005专用分页的存储过程,当数据量达到2千多万的时候,效率相当的低,每次执行都要8秒左右(CPU:Q6600)。不过在2百多万数据量的情况下性能还是蛮不错的,在网上找了找,发现这下面的这两个,其实还是一个,不过后面那个是灵活了许多,仅供参考。 
复制  保存
/****** 
Object:  StoredProcedure [dbo].[GetRecordFromPage]   
Script Date: 07/23/2008 18:42:05 
******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
/*
  函数名称: GetRecordFromPage
  函数功能: 获取指定页的数据
  参数说明: @tblName      包含数据的表名
           @fldName      关键字段名
           @PageSize     每页记录数
           @PageIndex    要获取的页码
           @OrderType    排序类型, 0 - 升序, 1 - 降序
           @strWhere     查询条件 (注意: 不要加 where)
  */
ALTER PROCEDURE [dbo].[GetRecordFromPage]
    @tblName      varchar(255),       -- 表名

    @fldName      varchar(255),       -- 字段名

    @PageSize     int = 10,           -- 页尺寸

    @PageIndex    int = 1,            -- 页码

    @OrderType    bit = 0,            -- 设置排序类型, 非 0 值则降序

    @strWhere     varchar(2000) = ''  -- 查询条件 (注意: 不要加 where)

AS

declare @strSQL   varchar(6000)       -- 主语句

declare @strTmp   varchar(1000)       -- 临时变量

declare @strOrder varchar(500)        -- 排序类型


if @OrderType != 0
begin
    set @strTmp = '<(select min'
    set @strOrder = ' order by [' + @fldName +'] desc'
end
else
begin
    set @strTmp = '>(select max'
    set @strOrder = ' order by [' + @fldName +'] asc'
end

set @strSQL = 'select top ' + str(@PageSize) + ' * from ['
    + @tblName + '] where [' + @fldName + ']' + @strTmp + '(['
    + @fldName + ']) from (select top ' + str((@PageIndex-1)*@PageSize) + ' ['
    + @fldName + '] from [' + @tblName + ']' + @strOrder + ') as tblTmp)'
    + @strOrder

if @strWhere != ''
    set @strSQL = 'select top ' + str(@PageSize) + ' * from ['
        + @tblName + '] where [' + @fldName + ']' + @strTmp + '(['
        + @fldName + ']) from (select top ' + str((@PageIndex-1)*@PageSize) + ' ['
        + @fldName + '] from [' + @tblName + '] where ' + @strWhere + ' '
        + @strOrder + ') as tblTmp) and ' + @strWhere + ' ' + @strOrder

if @PageIndex = 1
begin
    set @strTmp = ''
    if @strWhere != ''
        set @strTmp = ' where (' + @strWhere + ')'

    set @strSQL = 'select top ' + str(@PageSize) + ' * from ['
        + @tblName + ']' + @strTmp + ' ' + @strOrder
end

exec (@strSQL)

这是我目前见过效率最高的,不过它的order by 是不是有点儿问题,不能指定某一个,还有没有返回总记录数。于是有了下面这个改进的。 

如果需要总页数还可以再改一下,不过目前已经足够用了,当然,当达到千万数据量后,比上面的效率上稍微低了一点点,不过还不算太坏, 

比较推荐下面这个。 
复制  保存
/****** 
Object:  StoredProcedure [dbo].[usp_GetRecordFromPage]    
Script Date: 07/23/2008 18:42:37 
******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE       PROCEDURE [dbo].[usp_GetRecordFromPage]
    @tblName       varchar(1000),        -- 表名

    @SelectFieldName    varchar(4000),              -- 要显示的字段名(不要加select)

    @strWhere       varchar(4000),              -- 查询条件(注意: 不要加 where)

    @OrderFieldName      varchar(255),               -- 排序索引字段名

    @PageSize       int ,                 -- 页大小

    @PageIndex      int = 1,                  -- 页码

    @iRowCount      int output,                 -- 返回记录总数

    @OrderType      bit = 0                  -- 设置排序类型, 非 0 值则降序

           
AS

declare @strSQL    varchar(4000)       -- 主语句

declare @strTmp    varchar(4000)        -- 临时变量

declare @strOrder  varchar(400)        -- 排序类型

declare @strRowCount    nvarchar(4000)      -- 用于查询记录总数的语句


set @OrderFieldName=ltrim(rtrim(@OrderFieldName))

if @OrderType != 0
begin
    set @strTmp = '<(select min'
    set @strOrder = ' order by ' + @OrderFieldName +' desc'
end
else
begin
    set @strTmp = '>(select max'
    set @strOrder = ' order by ' + @OrderFieldName +' asc'
end

set @strSQL = 'select top ' + str(@PageSize) + @SelectFieldName+' from '
    + @tblName + ' where ' + @OrderFieldName + @strTmp + '('
    + right(@OrderFieldName,len(@OrderFieldName)-charindex('.',@OrderFieldName)) + ') from (select top ' + str((@PageIndex-1)*@PageSize)
    + @OrderFieldName + ' from ' + @tblName  + @strOrder + ') as tblTmp)'
    + @strOrder

if @strWhere != ''
    set @strSQL = 'select top ' + str(@PageSize) + @SelectFieldName+' from '
        + @tblName + ' where ' + @OrderFieldName + @strTmp + '('
        + right(@OrderFieldName,len(@OrderFieldName)-charindex('.',@OrderFieldName)) + ') from (select top ' + str((@PageIndex-1)*@PageSize)
        + @OrderFieldName + ' from ' + @tblName + ' where ' + @strWhere + ' '
        + @strOrder + ') as tblTmp) and ' + @strWhere + ' ' + @strOrder

if @PageIndex = 1
begin
    set @strTmp = ''
    if @strWhere != ''
        set @strTmp = ' where ' + @strWhere

    set @strSQL = 'select top ' + str(@PageSize) + @SelectFieldName+' from '
        + @tblName + @strTmp + ' ' + @strOrder
end

exec(@strSQL)
 
if @strWhere!=''
begin
  set @strRowCount = 'select @iRowCount=count(*) from ' + @tblName+' where '+@strWhere
end
else
begin
  set @strRowCount = 'select @iRowCount=count(*) from ' + @tblName
end

exec sp_executesql @strRowCount,N'@iRowCount int out',@iRowCount out
posted @ 2011-07-05 16:36  董雨  阅读(237)  评论(0编辑  收藏  举报