T-sql:如何执行优化的分页?

时间:2010-02-06 18:50:25

标签: sql-server performance tsql pagination

我编写了以下代码,它工作正常,但如果表包含一百万条记录,则需要3秒才能完成。有没有办法优化以下代码。

DBCC DROPCLEANBUFFERS; 
DBCC FREEPROCCACHE;

DECLARE @Page_Size int;
DECLARE @Page_Number int;
DECLARE @Lower_Bound int;
DECLARE @Upper_Bound int;

SET @Page_Size = 30;
SET @Page_Number = 30000;
SET @Lower_Bound = (@Page_Number - 1) * @Page_Size;
--SET @Upper_Bound = @Page_Number * @Page_Size;


WITH Customers AS--(Row_Numbr, Record_Id, First_Name, 
        Middle_Name, Last_Name, Email, Telephone) AS 
(

    SELECT ROW_NUMBER() 
        OVER 
         (ORDER BY Account.Customer.Record_Id) AS Row_Numbr, * 
    FROM Account.Customer 
)

SELECT top(@Page_Size) * 
FROM Customers 
WHERE Row_Numbr > @Lower_Bound-- 
    AND Row_Numbr <= @Upper_Bound -- This is suppose to be faster
--SELECT * FROM Customers 
--WHERE Row_Numbr > @Lower_Bound  
--   AND Row_Numbr <= @Upper_Bound

3 个答案:

答案 0 :(得分:14)

首先,为什么DBCC DROPCLEANBUFFERS;?这是缓冲池的硬冷复位。除非您想测量和调整硬盘IO性能,否则没有人关心冷缓存的性能。这是您的系统如何工作。缓冲池中的缓存页面是数据库中 最关键的性能方面,您可以解决这个问题。它就像没有引擎一样出现在法拉利,并问为什么这么慢。对于性能测量,您应该完全执行相反:运行查询4-5次以预热缓存,然后进行测量。

第二,你的桌面结构是什么?表Account.Customer表集群索引是Record_id的顺序吗?如果不是,无论你如何表达你的T-SQL,你都永远无法获得你想要的性能。

最后但并非最不重要的是,你有什么系统?它是否有足够的RAM来将整个数据库缓存在内存中?如果不是,请购买更多内存。是否有其他进程争夺内存,如IIS / Asp?如果是,请将它们踢出自己的服务器,如果性能很重要,则应该ever ever run the database on the same host as the web server

对于替代快速分页,请考虑密钥集驱动的解决方案:

/* moving up */
SELECT top(@Page_Size) * 
FROM Account.Customer  
WHERE Record_Id > @lastPageRecordId
ORDER BY Record_Id;

/* moving down */
SELECT top(@Page_Size) * 
FROM Account.Customer  
WHERE Record_Id < @firstPageRecordId
ORDER BY Record_Id DESC;

键集驱动的解决方案可以直接搜索到最后一个位置,然后使用聚簇索引键位置扫描下一个/上一个页面。分页逻辑(状态)必须记住正在显示的页面上的最后一个和第一个键,以便从那里继续,而不是记住页码。

基于行数的解决方案(以及MySQL中的LIMIT)效率低于基于密钥集的解决方案,因为它们总是需要对记录进行计数以定位自己,而不是像键集一样直接寻找位置。

答案 1 :(得分:2)

我使用这个存储过程:

CREATE PROCEDURE sp_PagedItems
    (
     @Page int,
     @RecsPerPage int
    )
AS

-- We don't want to return the # of rows inserted
-- into our temporary table, so turn NOCOUNT ON
SET NOCOUNT ON


--Create a temporary table
CREATE TABLE #TempItems
(
    ID int IDENTITY,
    Name varchar(50),
    Price currency
)


-- Insert the rows from tblItems into the temp. table
INSERT INTO #TempItems (Name, Price)
SELECT Name,Price FROM tblItem ORDER BY Price

-- Find out the first and last record we want
DECLARE @FirstRec int, @LastRec int
SELECT @FirstRec = (@Page - 1) * @RecsPerPage
SELECT @LastRec = (@Page * @RecsPerPage + 1)

-- Now, return the set of paged records, plus, an indiciation of we
-- have more records or not!
SELECT *,
       MoreRecords =
    (
     SELECT COUNT(*)
     FROM #TempItems TI
     WHERE TI.ID >= @LastRec
    )
FROM #TempItems
WHERE ID > @FirstRec AND ID < @LastRec


-- Turn NOCOUNT back OFF
SET NOCOUNT OFF

答案 2 :(得分:1)

如果某人正在使用SQL Server 2012 - 在ORDER BY子句中添加了一项新功能,则可以查询设置数据的优化,使用T-SQL以及整个执行中的任何人编写数据分页,使工作更轻松在SQL Server中规划。参考here