Методы пейджинга SQL Server 2008?

Мне нужно работать с потенциально большим списком записей, и я был Googling для того, чтобы избежать выбора всего списка, вместо этого я хочу, чтобы пользователи выбирали страницу (например, от 1 до 10) и отображали записи соответственно.

Скажем, для 1000 записей у меня будет 100 страниц по 10 записей, и сначала будут отображаться самые последние 10 записей, а затем, если пользователь нажмет на страницу 5, он отобразит записи от 41 до 50.

Можно ли добавить номер строки в каждую запись, а затем запрос на основе номера строки? Есть ли лучший способ достичь результата поискового вызова без слишком больших издержек? До сих пор эти методы, описанные здесь, выглядят наиболее перспективными:

http://developer.berlios.de/docman/display_doc.php?docid=739&group_id=2899

http://www.codeproject.com/KB/aspnet/PagingLarge.aspx

Ответ 1

Следующая хранимая процедура T-SQL представляет собой эффективную реализацию подкачки очень. Оптимизатор SQL может быстро найти первый идентификатор. Объедините это с использованием ROWCOUNT, и у вас есть подход, который эффективен и эффективен с точки зрения эффективности. Для таблицы с большим количеством строк она, безусловно, превосходит любой подход, который я видел, используя временную таблицу или переменную таблицы.

NB: в этом примере я использую столбец последовательного идентификатора, но код работает в любом столбце, подходящем для сортировки страниц. Кроме того, разрывы последовательностей в используемом столбце не влияют на результат, поскольку код выбирает количество строк, а не значение столбца.

EDIT: если вы сортируете столбец с потенциально не уникальными значениями (например, LastName), добавьте второй столбец в предложение Order By, чтобы снова сделать уникальные значения сортировки.

CREATE  PROCEDURE dbo.PagingTest
(
    @PageNumber int,
    @PageSize int
)
AS

DECLARE @FirstId int, @FirstRow int

SET @FirstRow = ( (@PageNumber - 1) * @PageSize ) + 1
SET ROWCOUNT @FirstRow

-- Add check here to ensure that @FirstRow is not
-- greater than the number of rows in the table.

SELECT   @FirstId = [Id]
FROM     dbo.TestTable
ORDER BY [Id]

SET ROWCOUNT @PageSize

SELECT   *
FROM     dbo.TestTable
WHERE    [Id] >= @FirstId
ORDER BY [Id]

SET ROWCOUNT 0
GO 

Ответ 2

Если вы используете CTE с двумя столбцами row_number() - один отсортированный asc, один desc, вы получаете номера строк для подкачки, а также общие записи, добавляя два столбца row_number.

create procedure get_pages(@page_number int, @page_length int)
as
    set nocount on;

    with cte as
    (
        select 
            Row_Number() over (order by sort_column desc) as row_num
            ,Row_Number() over (order by sort_column) as inverse_row_num
            ,id as cte_id
        From my_table
    )
    Select 
        row_num+inverse_row_num as total_rows
        ,*  
    from CTE inner join my_table
        on cte_id=df_messages.id
    where row_num between 
        (@page_number)*@page_length 
        and (@page_number+1)*@page_length
    order by rownumber

Ответ 3

Использование OFFSET

Другие объяснили, как функция ранжирования ROW_NUMBER() OVER() может использоваться для выполнения страниц. Стоит отметить, что SQL Server 2012, наконец, включил поддержку стандарта SQL OFFSET .. FETCH:

SELECT first_name, last_name, score
FROM players
ORDER BY score DESC
OFFSET 40 ROWS FETCH NEXT 10 ROWS ONLY

Если вы используете SQL Server 2012, а обратная совместимость - это не проблема, вам, вероятно, следует предпочесть этот раздел, поскольку он будет выполняться более оптимально SQL Server в угловых случаях.

Использование метода SEEK

Существует совершенно другой, гораздо более быстрый способ выполнения подкачки в SQL. Это часто называют "методом поиска", как описано в этот пост в блоге здесь.

SELECT TOP 10 first_name, last_name, score
FROM players
WHERE (score < @previousScore)
   OR (score = @previousScore AND player_id < @previousPlayerId)
ORDER BY score DESC, player_id DESC

Значения @previousScore и @previousPlayerId являются соответствующими значениями последней записи с предыдущей страницы. Это позволяет вам получать "следующую" страницу. Если направление ORDER BY ASC, просто используйте >.

С помощью вышеуказанного метода вы не можете сразу перейти на страницу 4, не предварительно извлек предыдущие 40 записей. Но часто вы не хотите так далеко прыгать. Вместо этого вы получаете гораздо более быстрый запрос, который мог бы получать данные в постоянное время, в зависимости от вашей индексации. Кроме того, ваши страницы остаются "стабильными", независимо от того, изменяются ли базовые данные (например, на странице 1, когда вы находитесь на странице 4).

Это лучший способ реализовать пейджинг при ленивой загрузке большего количества данных в веб-приложениях, например.

Обратите внимание, что "метод поиска" также называется подкачкой набора ключей.

Ответ 4

Попробуйте что-то вроде этого:

declare @page int = 2
declare @size int = 10

declare @lower int =  (@page - 1) * @size
declare @upper int =  (@page    ) * @size

select * from (
select 
    ROW_NUMBER() over (order by some_column) lfd,
* from your_table
) as t
 where lfd between @lower and @upper
 order by some_column

Ответ 5

Здесь обновлена ​​версия кода @RoadWarrior, используя TOP. Производительность идентична и очень быстрая. Убедитесь, что у вас есть индекс в TestTable.ID

CREATE PROC dbo.PagingTest
    @SkipRows int,
    @GetRows int
AS
DECLARE @FirstId int

SELECT   TOP (@SkipRows) 
         @FirstId = [Id]
FROM     dbo.TestTable
ORDER BY [Id]

SELECT   TOP (@GetRows) *
FROM     dbo.TestTable
WHERE    [Id] >= @FirstId
ORDER BY [Id]

GO 

Ответ 6

Попробуйте это

Declare @RowStart int, @RowEnd int;


SET @RowStart = 4;
SET @RowEnd = 7; 

With MessageEntities As 
(
    Select ROW_NUMBER() Over (Order By [MESSAGE_ID]) As Row, [MESSAGE_ID]
    From [TBL_NAFETHAH_MESSAGES]
)
Select  m0.MESSAGE_ID, m0.MESSAGE_SENDER_NAME,
        m0.MESSAGE_SUBJECT, m0.MESSAGE_TEXT
From MessageEntities M
    Inner Join [TBL_NAFETHAH_MESSAGES] m0 on M.MESSAGE_ID = m0.MESSAGE_ID
Where M.Row Between @RowStart AND @RowEnd
Order By M.Row Asc
GO

Ответ 7

Почему бы не использовать рекомендуемое решение:

SELECT VALUE product FROM           Продукт AdventureWorksEntities.Products AS           заказать по продукту .ListPrice SKIP @skip LIMIT @limit