以下是sqlserver2000中经常用到分页存储过程CREATE procedure p_splitpage   
@sql nvarchar(4000),--要执行的sql语句
@page int=1, --要显示的页码
@pageSize int,--每页的大小
@pageCount int=0 out,--总页数
@recordCount int=0 out--总记录数
as
set nocount on
declare @p1 int
exec sp_cursoropen @p1 output,@sql,@scrollopt=1,@ccopt=1,@rowcount=@pagecount output
set @recordCount = @pageCount
select @pagecount=ceiling(1.0*@pagecount/@pagesize),@page=(@page-1)*@pagesize+1
exec sp_cursorfetch @p1,16,@page,@pagesize 
exec sp_cursorclose @p1
GO
问题是:在sqlserver2005或2008中,要求不改变原来存储过程中的参数,如何改写上面的存储过程能大大提高查询分页的执行效率?
如果能把原来的存储过程和改进后的存储过程在不同版本中花费的时间对比列出来就更好了。解决立即给分!

解决方案 »

  1.   

    参考邹老大写的存储过程:
    IF OBJECT_ID(N'dbo.p_show') IS NOT NULL
        DROP PROCEDURE dbo.p_show
    GO/**//*--实现分页的通用存储过程    显示指定表、视图、查询结果的第X页
        对于表中主键或标识列的情况,直接从原表取数查询,其它情况使用临时表的方法
        如果视图或查询结果中有主键,不推荐此方法
        如果使用查询语句,而且查询语句使用了order by,则查询语句必须包含top 语句最后更新时间: 2008.01.20
    --邹建 2003.09(引用请保留此信息)--*//**//*--调用示例
    EXEC dbo.p_show 
        @QueryStr = N'tb',
        @PageSize = 5,
        @PageCurrent = 3,
        @FdShow = 'id, colid, name',
        @FdOrder = 'colid, name'
    select id, colid from tb
    order by colid, name
    EXEC dbo.p_show 
        @QueryStr = N'
    SELECT TOP 100 PERCENT 
        * 
    FROM dbo.sysobjects
    ORDER BY xtype',
        @PageSize = 5,
        @PageCurrent = 2,
        @FdShow = 'name, xtype',
        @FdOrder = 'xtype, name'
    --*/
    CREATE PROC dbo.p_show
        @QueryStr nvarchar(4000),        -- 表名、视图名、查询语句
        @PageSize int=10,                -- 每页的大小(行数)
        @PageCurrent int=1,                -- 要显示的页
        @FdShow nvarchar (4000) = N'',    -- 要显示的字段列表,如果查询结果不需要标识字段,需要指定此值,且不包含标识字段
        @FdOrder nvarchar (1000) = N''    -- 排序字段列表
    AS
    SET NOCOUNT ON
    DECLARE
        @FdName sysname,    --表中的主键或表、临时表中的标识列名
        @Id1 sysname,        --开始和结束的记录号
        @Id2 sysname,
        @Obj_ID int            --对象ID--表中有复合主键的处理
    DECLARE
        @strfd nvarchar(2000),        --复合主键列表
        @strjoin nvarchar(4000),    --连接字段
        @strwhere nvarchar(2000)    --查询条件
    SELECT
        @Obj_ID = OBJECT_ID(@QueryStr),
        @FdShow = CASE 
                    WHEN @FdShow > N'' THEN N' ' + @FdShow
                    ELSE N' *'
                END,
        @FdOrder = CASE
                    WHEN @FdOrder > N'' THEN N' ORDER BY ' + @FdOrder
                    ELSE N' ' 
                END,
        @QueryStr = CASE
                    WHEN @Obj_ID IS NULL THEN N' (' + @QueryStr + N')A'
                    ELSE N' ' + @QueryStr
                END-- 如果显示第一页,可以直接用 top 来完成
    IF @PageCurrent = 1    
    BEGIN
        SELECT 
            @Id1 = CAST(@PageSize as varchar(20))
        EXEC(N'
    SELECT TOP ' + @Id1 + N'
        ' + @FdShow + N'
    FROM ' + @QueryStr + N'
    ' + @FdOrder
    )
        RETURN
    END-- 如果是表, 则检查表中是否有标识更或主键
    IF @Obj_ID IS NULL OR OBJECTPROPERTY(@Obj_ID, 'IsTable') = 0
        GOTO lb_usetemp
    ELSE
    BEGIN
        SELECT
            @Id1 = CAST(@PageSize as varchar(20)),
            @Id2 = CAST((@PageCurrent - 1) * @PageSize as varchar(20))    -- 标识列
        SELECT
            @FdName = name
        FROM dbo.syscolumns
        WHERE id = @Obj_ID
            AND status = 0x80
        IF @@ROWCOUNT = 0            --如果表中无标识列,则检查表中是否有主键
        BEGIN
            DECLARE
                @pk_number int        SELECT
                @strfd = N'',
                @strjoin = N'',
                @strwhere = N''        SELECT
                @strfd = @strfd 
                        + N',' + QUOTENAME(name),
                @strjoin = @strjoin 
                        + N' AND A.' + QUOTENAME(name) 
                        + N'=B.' +  QUOTENAME(name),
                @strwhere = @strwhere 
                        + N' AND B.' + QUOTENAME(name) + N' IS NULL'
            FROM(
                SELECT
                    IX.id, IX.indid,
                    IXC.colid, ixc.keyno,
                    C.name
                FROM dbo.sysobjects O, 
                    dbo.sysindexes IX,
                    dbo.sysindexkeys IXC,
                    dbo.syscolumns C
                WHERE O.parent_obj = @Obj_ID
                    AND O.xtype = 'PK'
                    AND O.name = IX.name
                    AND IX.id = @Obj_ID
                    AND IX.id = IXC.id
                    AND IX.indid = IXC.indid
                    AND IXC.id = C.id
                    AND IXC.colid = C.colid
            )A
            ORDER BY keyno        SELECT
                @pk_number = @@ROWCOUNT,            
                @strfd = STUFF(@strfd, 1, 1, N''),
                @strjoin = STUFF(@strjoin, 1, 5, N''),
                @strwhere = STUFF(@strwhere, 1, 5, N'')                    IF @pk_number = 0
                GOTO lb_usetemp        --如果表中无主键,则用临时表处理
            ELSE IF @pk_number = 1
            BEGIN
                SELECT
                    @FdName = @strfd
                GOTO lb_useidentity    -- 使用单一主键
            END
            ELSE
                GOTO lb_usepk        -- 使用复合主键
        END
    END/**//*--使用标识列或主键为单一字段的处理方法--*/
    lb_useidentity:    
    EXEC(N'
    SELECT TOP ' + @Id1 + N'
        ' + @FdShow + N'
    FROM '+@QueryStr + N'
    WHERE ' + @FdName + ' NOT IN(
            SELECT TOP ' + @Id2 + N'
                ' + @FdName + '
            FROM ' + @QueryStr + N'
            ' + @FdOrder + N')
    ' + @FdOrder + N'
    ')
    RETURN/**//*--表中有复合主键的处理方法--*/
    lb_usepk:        
    EXEC(N'
    SELECT 
        ' + @FdShow + N'
    FROM(
        SELECT TOP ' + @Id1 + N'
            A.*
        FROM ' + @QueryStr + N' A
            LEFT JOIN(
                    SELECT TOP ' + @Id2 + N'
                        ' + @strfd + N' 
                    FROM ' + @QueryStr + N'
                    ' + @FdOrder + N'
                )B
                    ON ' + @strjoin + N'
        WHERE ' + @strwhere + N'
        ' + @FdOrder + N'
    )A
    ' + @FdOrder + N'
    ')
    RETURN/**//*--用临时表处理的方法--*/
    lb_usetemp:        
    SELECT
        @FdName = QUOTENAME(N'ID_' + CAST(NEWID() as varchar(40))),
        @Id1 = CAST(@PageSize * (@PageCurrent-1) as varchar(20)),
        @Id2 = CAST(@PageSize * @PageCurrent-1 as varchar(20))EXEC(N'
    SELECT 
        ' + @FdName + N' = IDENTITY(int, 0, 1),
        ' + @FdShow + N'
    INTO #tb
    FROM(
        SELECT TOP 100 PERCENT 
            * 
        FROM ' + @QueryStr + N'
        ' + @FdOrder + N'
    )A
    ' + @FdOrder + N'SELECT 
        ' + @FdShow + N'
    FROM #tb 
    WHERE ' + @FdName + ' BETWEEN ' + @Id1 + ' AND ' + @Id2 + N'
    '
    )
    GO
      

  2.   

    特殊分页处理:
    http://blog.csdn.net/zjcxc/archive/2004/12/22/225446.aspx
      

  3.   

    要求保留参数一致哦。
    @sql nvarchar(4000),--要执行的sql语句 
    @page int=1, --要显示的页码 
    @pageSize int,--每页的大小 
    @pageCount int=0 out,--总页数 
    @recordCount int=0 out--总记录数 后面的怎么写?
      

  4.   

    刚学习了一下SQL2005的,想改成row_number()的分页,发现效率不高,所以建议优化一下索引效率就不错了,原文如下:体验Sql Server2005下Row_Number分页存储过程(2008-04-24 16:58:57)标签:it   分类:IT技术文章 
    如何使用Sql Server2005的Row_Number分页存储过程功能,就让我们来试试吧!原来在sql server2000下所用的分页存储过程与使用Row_Number()编写的存储过程在Sql Server2005上的执行效率...数据表:
    REATE TABLE [dbo].[test](
    [UserId] [int] Primary Key IDENTITY(1,1) ,
    [UserName] [nvarchar](256) ,
    [Sex] [varchar](50) NOT NULL,
    [Age] [int] NOT NULL,
    [Address] [varchar](100) ,
    [status] [bit] NULL,
    [Email] [varchar](100) ,
    [InsertDate] [datetime] NOT NULL
    )
    插入1000k记录
    use temp
    Godeclare @n int
    set @n = 0
    while @n<1000000
    BEGIN
    Insert Into test(UserName,Sex,Age,Address,status,Email,InsertDate)
    Values('bbisky','男','25','中国传媒大学现代远程教育中心',1,'[email protected]',getdate())
    Select @n = @n+1
    END两个存储过程原来使用Top的分页存储过程
    Create proc [dbo].[test_PageById]
    (
    @pageIndex int,
    @pageSize int
    )
    AS
    SELECT TOP(@pageSize) *
    FROM test
    WHERE UserId <
    (SELECT MIN(UserId) FROM (
    SELECT TOP ((@pageIndex-1) * @pageSize) UserId
    FROM test
    ORDER BY UserId DESC)B )
    ORDER BY UserId DESC使用Row_number的存储过程
    CREATE proc [dbo].[test_PageByRowNumber]
    (
    @pageIndex int,
    @pageSize int
    )
    AS
    DECLARE @startRow int, @endRow int
    Set @startRow = (@pageIndex - 1) * @pageSize +1
    SET @endRow = @startRow + @pageSize -1
    SELECT*
    FROM (
    SELECT *,ROW_NUMBER() OVER (ORDER BY UserId DESC) AS RowNumber
    FROM test ) T
    WHERE T.RowNumber BETWEEN @startRow AND @endRow测试和结果SET STATISTICS io ON
    SET STATISTICS time ON
    go
    EXEC test_PageByRowNumber 1000,50 --RowNumber存储过程
    go
    SET STATISTICS profile OFF
    SET STATISTICS io OFF
    SET STATISTICS time OFF
    SQL Server 分析和编译时间:
    CPU 时间 = 0 毫秒,占用时间 = 1 毫秒。
    SQL Server 分析和编译时间:
    CPU 时间 = 0 毫秒,占用时间 = 1 毫秒。SQL Server 执行时间:
    CPU 时间 = 0 毫秒,占用时间 = 1 毫秒。SQL Server 执行时间:
    CPU 时间 = 0 毫秒,占用时间 = 1 毫秒。(50 行受影响)
    表 'test'。扫描计数 1,逻辑读取 50098 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。SQL Server 执行时间:
    CPU 时间 = 219 毫秒,占用时间 = 213 毫秒。SQL Server 执行时间:
    CPU 时间 = 219 毫秒,占用时间 = 213 毫秒。
    SQL Server 分析和编译时间:
    CPU 时间 = 0 毫秒,占用时间 = 1 毫秒。SQL Server 执行时间:
    CPU 时间 = 0 毫秒,占用时间 = 1 毫秒。SQL Server 执行时间:
    CPU 时间 = 0 毫秒,占用时间 = 1 毫秒。SET STATISTICS io ON
    SET STATISTICS time ON
    go
    EXEC test_PageById 1000,50 --执行使用top语句的存储过程
    go
    SET STATISTICS profile OFF
    SET STATISTICS io OFF
    SET STATISTICS time OFF
    SQL Server 分析和编译时间:
    CPU 时间 = 0 毫秒,占用时间 = 1 毫秒。
    SQL Server 分析和编译时间:
    CPU 时间 = 0 毫秒,占用时间 = 1 毫秒。(50 行受影响)
    表 'test'。扫描计数 2,逻辑读取 153 次,物理读取 0 次,预读 93 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。SQL Server 执行时间:
    CPU 时间 = 16 毫秒,占用时间 = 26 毫秒。SQL Server 执行时间:
    CPU 时间 = 16 毫秒,占用时间 = 26 毫秒。
    SQL Server 分析和编译时间:
    CPU 时间 = 0 毫秒,占用时间 = 1 毫秒。SQL Server 执行时间:
    CPU 时间 = 0 毫秒,占用时间 = 1 毫秒。SQL Server 执行时间:
    CPU 时间 = 0 毫秒,占用时间 = 1 毫秒。至此可以很直观的看出差距了.使用原来的Top子句的存储过程比使用Row_Number()的存储过程执行时间快了将近10倍.....其实直接分析语句也可以看出,Row_Number()的效率不会是最高的,因为它必须先为100万条记条生成RowNumber,自然不会快到哪里去了.
    不过前者的适应范围有些限制,即必须有一个为数字的唯一字段,如果使用uniqueidentifier为主键的话则不能使用了. Row_Number分页有很好的通用性和直观易用性,对于数据量较少来说,二者不会有很大的区别,具体是使用就根据我们的实际需要拉使用拉! ⊙该文章转自[汕头电脑技术网[www.dnao168.cn] 原文链接:http://www.dnao168.cn/html/bianchengtiandi/shujuku/20080411/792.html
      

  5.   

    如何改写上面的存储过程能大大提高查询分页的执行效率? 
    -->>还真以为2005和2008就是救世主啊。还是建立索引等等手段把
      

  6.   

    Create procedure [dbo].[uspCustomPaging] 
        @TableName varchar(50),                  --表或视图名
        @Fields varchar(5000) = '*',              --字段名(全部字段为*)
        @OrderFields varchar(5000),            --排序字段(必须!支持多字段,建议建索引)
        @SqlWhere varchar(5000) = '',         --条件语句(如and Name='a')
        @PageSize int,                                     --每页多少条记录
        @PageIndex int = 1 ,                           --指定当前为第几页
        @TotalPages int output                    --返回总页数 
    as
    begin
        declare @sql nvarchar(4000)
        declare @TotalRecords int       --计算总记录数及总页数     
        set @sql = 'select @TotalRecords = count(*) from ' + @TableName + ' where 1=1 ' + @sqlWhere
        exec sp_executesql @sql,N'@totalRecords int output',@TotalRecords output
        select @TotalPages=CEILING((@TotalRecords+0.0)/@PageSize)    --处理页数超出范围情况
        if @PageIndex<=0 
            set @PageIndex = 1
        if @PageIndex>@TotalPages
            set @PageIndex = @TotalPages    set @sql = 'select '+ @Fields + ' from (select top(@PageIndex*@PageSize) ' + @Fields + ',row_number() over(order by ' + @OrderFields + ') as rowNumber from ' + @TableName + ' where 1=1 ' + @SqlWhere + ') t where t.rowNumber >= ((@PageIndex-1)*@PageSize+1)'
        
        --print @Sql   
        exec sp_executesql @sql,N'@PageIndex int, @PageSize int',@PageIndex,@PageSize  
    end
      

  7.   

    总记录数count就是了,还要特意加上去?显示了总页数啊