Методы пейджинга 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