分页存储过程该如何优化

byevilangel 发布于 2013/04/17 15:05
阅读 485
收藏 0

该存储过程该如何优化,才能保证查询1000000条数据能在10秒完成。(此存储过程是个通用的分页存储过程)

 

ALTER procedure [dbo].[usp_fPage]
( 
@tblName     nvarchar(4000),         ----要显示的表或多个表的连接
@fldName     nvarchar(4000) = '*',   ----要显示的字段列表
@pageSize    int = 10,    ----每页显示的记录个数
@page        int = 1,    ----当前页码
@pageCount    int = 1 output,  ----查询结果分页后的总页数
@Counts    int = 1 output,          ----查询到的记录数
@fldSort    nvarchar(4000),   ----排序字段列表或条件
@Sort        bit = 0,        ----排序方法,0为升序,1为降序(如果是多字段排列Sort指代最后一个排序字段的排列顺序(最后一个排序字段不加排序标记)--程序传参如:' SortA Asc,SortB Desc,SortC ')
@strCondition    nvarchar(4000) ,    ----查询条件,不需where
@ID        nvarchar(4000),        ----主表的主键
@Dist                 bit = 0           ----是否添加查询字段的 DISTINCT 默认0不添加/1添加
)
 
AS
 
SET NOCOUNT ON  --设置不返回计数Declare @sqlTmp nvarchar(1000)        ----存放动态生成的SQL语句
Declare @strTmp nvarchar(1000)        ----存放取得查询结果总数的查询语句
Declare @strID     nvarchar(1000)        ----存放取得查询开头或结尾ID的查询语句
 
Declare @strSortType nvarchar(10)    ----数据排序规则A
Declare @strFSortType nvarchar(10)    ----数据排序规则B
 
Declare @SqlSelect nvarchar(50)         ----对含有DISTINCT的查询进行SQL构造
Declare @SqlCounts nvarchar(50)          ----对含有DISTINCT的总数查询进行SQL构造--判断列是否传入
if @fldName is null or @fldName=''
begin
 set @fldName='*'
end
 
if @Dist  = 0
begin
    set @SqlSelect = 'Select '
    set @SqlCounts = 'Count(*)'
end
else
begin
    set @SqlSelect = 'Select Distinct '
    set @SqlCounts = 'Count(DISTINCT '+@ID+')' end
--排序列(如果没有传入排序列,默认按主键排序)
if @fldSort is null or @fldSort=''
begin
 set @fldSort=@ID
end
--排序方式
if @Sort=0
begin
    set @strFSortType=' ASC '
    set @strSortType=' DESC '
end
else
begin
    set @strFSortType=' DESC '
    set @strSortType=' ASC '
end--------生成查询语句--------
--此处@strTmp为取得查询结果数量的语句
if @strCondition is null or @strCondition=''     --没有设置显示条件
begin
    set @sqlTmp = @SqlSelect+ @fldName + ' FROM ' + @tblName
    set @strTmp = @SqlSelect+' @Counts='+@SqlCounts+' FROM '+@tblName     set @strID = ' FROM ' + @tblName
end
else
begin
    set @sqlTmp = @SqlSelect+ @fldName + ' FROM ' + @tblName + ' where 1=1 ' + @strCondition
    set @strTmp = @SqlSelect+' @Counts='+@SqlCounts+' FROM '+@tblName + ' where 1=1 ' + @strCondition
    set @strID = ' FROM ' + @tblName + ' where 1=1 ' + @strCondition
end
----取得查询结果总数量-----
exec sp_executesql @strTmp,N'@Counts int out ',@Counts out
declare @tmpCounts int
if @Counts = 0
    set @tmpCounts = 1
else
    set @tmpCounts = @Counts
--取得分页总数
set @pageCount=(@tmpCounts+@pageSize-1)/@pageSize
/**//**当前页大于总页数 取最后一页**/
    if @page>@pageCount
        set @page=@pageCount
--/*-----数据分页2分处理-------*/
    declare @pageIndex int --总数/页大小
    declare @lastcount int --总数%页大小    set @pageIndex = @tmpCounts/@pageSize
    set @lastcount = @tmpCounts%@pageSize    if @lastcount > 0
        set @pageIndex = @pageIndex + 1
    else
        set @lastcount = @pagesize
    --//***显示分页
    if @strCondition is null or @strCondition=''     --没有设置显示条件
    begin
        if @pageIndex<2 or @page<=@pageIndex / 2 + @pageIndex % 2   --前半部分数据处理
            begin 
                if @page=1  --首页
                    set @strTmp=@SqlSelect+' top '+ CAST(@pageSize as NVARCHAR(100))+' '+ @fldName+' FROM '+@tblName                        
                        +' order by '+ @fldSort +' '+ @strFSortType
                else
                begin                    
                    set @strTmp=@SqlSelect+' top '+ CAST(@pageSize as NVARCHAR(100))+' '+ @fldName+' FROM '+@tblName                         +' where '+@ID+' <(select min('+ @ID +') FROM ('+ @SqlSelect+' top '+ CAST(@pageSize*(@page-1) as Varchar(20)) +' '+ @ID +' FROM '+@tblName                         +' order by '+ @fldSort +' '+ @strFSortType+') AS TBMinID)'
                        +' order by '+ @fldSort +' '+ @strFSortType
                end    
            end
        else
            begin
            set @page = @pageIndex-@page+1 --后半部分数据处理
                if @page <= 1 --最后一页数据显示                
                    set @strTmp=@SqlSelect+' * FROM ('+@SqlSelect+' top '+ CAST(@lastcount as NVARCHAR(100))+' '+ @fldName+' FROM '+@tblName                         +' order by '+ @fldSort +' '+ @strSortType+') AS TempTB'+' order by '+ @fldSort +' '+ @strFSortType 
                else
                    set @strTmp=@SqlSelect+' * FROM ('+@SqlSelect+' top '+ CAST(@pageSize as NVARCHAR(100))+' '+ @fldName+' FROM '+@tblName                         +' where '+@ID+' >(select max('+ @ID +') FROM ('+ @SqlSelect+' top '+ CAST(@pageSize*(@page-2)+@lastcount as Varchar(20)) +' '+ @ID +' FROM '+@tblName                         +' order by '+ @fldSort +' '+ @strSortType+') AS TBMaxID)'
                        +' order by '+ @fldSort +' '+ @strSortType+') AS TempTB'+' order by '+ @fldSort +' '+ @strFSortType 
            end
    end
    else --有查询条件
    begin
        if @pageIndex<2 or @page<=@pageIndex / 2 + @pageIndex % 2   --前半部分数据处理
        begin
                if @page=1 --首页
                    set @strTmp=@SqlSelect+' top '+ CAST(@pageSize as NVARCHAR(100))+' '+ @fldName+' FROM '+@tblName                        
                        +' where 1=1 ' + @strCondition + ' order by '+ @fldSort +' '+ @strFSortType
                else
                begin                    
                    set @strTmp=@SqlSelect+' top '+ CAST(@pageSize as NVARCHAR(100))+' '+ @fldName+' FROM '+@tblName                         +' where '+@ID+' <(select min('+ @ID +') FROM ('+ @SqlSelect+' top '+ CAST(@pageSize*(@page-1) as Varchar(20)) +' '+ @ID +' FROM '+@tblName                         +' where (1=1) ' + @strCondition +' order by '+ @fldSort +' '+ @strFSortType+') AS TBMinID)'
                        +' '+ @strCondition +' order by '+ @fldSort +' '+ @strFSortType
                end            
        end
        else
        begin 
            set @page = @pageIndex-@page+1 --后半部分数据处理
            if @page <= 1 --最后一页数据显示
                    set @strTmp=@SqlSelect+' * FROM ('+@SqlSelect+' top '+ CAST(@lastcount as NVARCHAR(100))+' '+ @fldName+' FROM '+@tblName                         +' where (1=1) '+ @strCondition +' order by '+ @fldSort +' '+ @strSortType+') AS TempTB'+' order by '+ @fldSort +' '+ @strFSortType                     
            else
                    set @strTmp=@SqlSelect+' * FROM ('+@SqlSelect+' top '+ CAST(@pageSize as NVARCHAR(100))+' '+ @fldName+' FROM '+@tblName                         +' where '+@ID+' >(select max('+ @ID +') FROM ('+ @SqlSelect+' top '+ CAST(@pageSize*(@page-2)+@lastcount as Varchar(20)) +' '+ @ID +' FROM '+@tblName                         +' where (1=1) '+ @strCondition +' order by '+ @fldSort +' '+ @strSortType+') AS TBMaxID)'
                        +' '+ @strCondition+' order by '+ @fldSort +' '+ @strSortType+') AS TempTB'+' order by '+ @fldSort +' '+ @strFSortType                
        end    
    end
------返回查询结果-----exec sp_executesql @strTmpSET NOCOUNT OFF --设置返回计数



加载中
0
XuMeijin
XuMeijin
LZ是什么数据库啊,貌似sql server吧?如果是2005以上的用系统的函数ROW_NUMBER(),100万的表几十到几百毫秒就可以了
0
byevilangel
byevilangel
是SQL Server 2008,要求查询100万条数据控制在10秒,然而,我无论怎么改,都必须12秒或12秒以上。所以看看能不能再优化一下语句
0
XuMeijin
XuMeijin

给你一段我系统自动生成的SQL 语句供你参考吧,

WITH Temp_Table AS (SELECT TempTable.* , ROW_NUMBER() OVER ( ORDER BY ConfirmTime) AS Temp_Row_Number FROM(SELECT DISTINCT ZQLQF_TicketBook.TicketBookID, ZQLQF_TicketBook.ConfirmTime FROM

ZQLQF_TicketBook WHERE ZQLQF_TicketBook.BookType = 1 AND ZQLQF_TicketBook.BillKind = 1 AND ZQLQF_TicketBook.ConfirmTime > '1900-1-1' AND ZQLQF_TicketBook.DrawTime = '1900-1-1') AS TempTable) SELECT

ZQLQF_TicketBook.* FROM ZQLQF_TicketBook JOIN Temp_Table ON ZQLQF_TicketBook.TicketBookID = Temp_Table.TicketBookID WHERE Temp_Row_Number BETWEEN 1 AND 17 ORDER BY Temp_Row_Number
SELECT COUNT(DISTINCT ZQLQF_TicketBook.TicketBookID) FROM ZQLQF_TicketBook WHERE ZQLQF_TicketBook.BookType = 1 AND ZQLQF_TicketBook.BillKind = 1 AND ZQLQF_TicketBook.ConfirmTime > '1900-1-1' AND

ZQLQF_TicketBook.DrawTime = '1900-1-1'

 

byevilangel
byevilangel
额,人比较笨,不知道你这生成的代码和我上面那存储过程生成的代码有什么区别
0
XuMeijin
XuMeijin
以上我的笔记本200万的表测试不到1秒,生成环境可能会多一些,但是也不大会10秒那么大(除非并发数特别多),内部系统类用这个足够了
0
李察德-泰森
李察德-泰森

动态的SQL语句,不能生成执行计划

byevilangel
byevilangel
额,,,不懂
0
XuMeijin
XuMeijin

WITH Temp_Table AS (SELECT TempTable.* FROM(SELECT DISTINCT ZQLQF_RoleRight.RoleRightID FROM ZQLQF_RoleRight JOIN ZQLQF_Role ON ZQLQF_RoleRight.RoleID = ZQLQF_Role.RoleID JOIN ZQLQF_LoginRole ON ZQLQF_Role.RoleID = ZQLQF_LoginRole.RoleID JOIN ZQLQF_Login ON ZQLQF_LoginRole.LoginID = ZQLQF_Login.LoginID WHERE ZQLQF_Login.LoginName = '***') AS TempTable) SELECT ZQLQF_RoleRight.* FROM ZQLQF_RoleRight JOIN Temp_Table ON ZQLQF_RoleRight.RoleRightID = Temp_Table.RoleRightID

多表的

根据自己的情况改吧,我们的系统SQL语句都是动态生成的

0
byevilangel
byevilangel
能否具体告诉我该怎么做?我对这个真心不懂。
0
李察德-泰森
李察德-泰森
先把你的脚本语法着色,太乱没心情看
0
XuMeijin
XuMeijin

你先查一下ROW_NUMBER()的用法

根据查询条件和排序字段生成临时表
WITH Temp_Table AS (SELECT TempTable.* , ROW_NUMBER() OVER ( ORDER BY ConfirmTime) AS Temp_Row_Number FROM(SELECT DISTINCT ZQLQF_TicketBook.TicketBookID, ZQLQF_TicketBook.ConfirmTime FROM ZQLQF_TicketBook WHERE ZQLQF_TicketBook.BookType = 1) AS TempTable)
从临时表中取出 第10页即第101到110的记录
SELECT ZQLQF_TicketBook.* FROM ZQLQF_TicketBook JOIN Temp_Table ON ZQLQF_TicketBook.TicketBookID = Temp_Table.TicketBookID WHERE Temp_Row_Number BETWEEN 101 AND 110 ORDER BY Temp_Row_Number
查询符合条件的总记录数以便程序计算总页数
SELECT COUNT(DISTINCT ZQLQF_TicketBook.TicketBookID) FROM ZQLQF_TicketBook WHERE ZQLQF_TicketBook.BookType = 1

 

0
XuMeijin
XuMeijin

--根据查询条件和排序字段生成临时表
WITH Temp_Table AS (SELECT 临时表.* , ROW_NUMBER() OVER ( ORDER BY 排序字段 AS 临时字段 FROM(SELECT DISTINCT 排序字段 FROM 实际表名 WHERE 查询条件) AS 临时表)
--从临时表中取出 第10页即第101到110的记录
SELECT 实际表名.* FROM 实际表名 JOIN 临时表 ON 实际表名.TicketBookID = 临时表.TicketBookID WHERE 临时字段 BETWEEN 101 AND 110 ORDER BY 临时字段
--查询符合条件的总记录数以便程序计算总页数
SELECT COUNT(DISTINCT 实际表名.TicketBookID) FROM 实际表名 WHERE 查询条件

这样能看明白了吧?再根据你的实际情况将查询条件、表名、每页显示记录、当前页码的信息用参数来代替做成存储过程

byevilangel
byevilangel
我查了14秒
返回顶部
顶部