T-sql: как выполнить оптимизированный пейджинг? - PullRequest
8 голосов
/ 06 февраля 2010

Я написал следующий код, он отлично работает, но для завершения требуется около 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 ]

14 голосов
/ 06 февраля 2010

Во-первых, почему DBCC DROPCLEANBUFFERS;? Это жесткий холодный сброс пула буферов. Если вы не хотите измерять и настраивать производительность ввода-вывода своих жестких дисков, никто не заботится о производительности холодного кэша. Это не как ваша система будет работать. Кэширование страниц в пуле буферов является наиболее важным аспектом производительности в базах данных , и вы это исправите. Это все равно что показываться в Ferrari без двигателя и спрашивать, почему так медленно. Для измерения производительности вы должны сделать ровно напротив : выполнить запрос 4-5 раз, чтобы прогреть кеш, а затем измерить.

Во-вторых, какова ваша структура таблицы? Является ли таблица Account.Customer порядком индекса кластера таблицы на Record_id? Если нет, вы никогда не получите желаемой производительности независимо от того, как вы выражаете свой T-SQL.

И последнее, но не менее важное: какая у вас система? Достаточно ли оперативной памяти для кэширования всей базы данных в памяти? Если нет, купите больше оперативной памяти. Существуют ли другие процессы, которые конкурируют за память, такие как IIS / Asp? Если да, выкиньте их на свой собственный сервер, и вам никогда не следует запускать базу данных на том же хосте, что и веб-сервер, , если важна производительность.

Для альтернативного быстрого пейджинга рассмотрите решения на основе набора ключей:

/* 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;

Решение, управляемое набором ключей, может искать прямо до последней позиции, а затем сканировать диапазон на следующей / предыдущей странице, используя позицию ключа кластеризованного индекса. Логика пейджинга (состояние) должна помнить последние и первые клавиши на отображаемой странице, чтобы продолжить оттуда, вместо запоминания номера страницы.

Решения на основе Rowcount (а также LIMIT в MySQL) менее эффективны, чем решения на основе набора ключей, потому что они всегда должны считать записи, чтобы позиционировать себя, вместо того, чтобы искать прямо в позиции, как это могут делать наборы ключей.

2 голосов
/ 07 февраля 2010

Я использую эту хранимую процедуру:

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
1 голос
/ 06 января 2015

Если кто-то использует SQL Server 2012 - в предложение ORDER BY была добавлена ​​новая функция для запроса оптимизации набора данных, упрощая работу с подкачкой данных для всех, кто пишет в T-SQL, а также для всего выполнения. Планирование в SQL Server. Ссылка здесь .

...