Вопрос

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

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

Упрощает ли LINQ решение?

Это было полезно?

Решение

В MS SQL Server 2005 и выше: СТРОКА_НОМЕР() кажется, работает:

Т-SQL:Пейджинг с помощью ROW_NUMBER()

DECLARE @PageNum AS INT;
DECLARE @PageSize AS INT;
SET @PageNum = 2;
SET @PageSize = 10;

WITH OrdersRN AS
(
    SELECT ROW_NUMBER() OVER(ORDER BY OrderDate, OrderID) AS RowNum
          ,OrderID
          ,OrderDate
          ,CustomerID
          ,EmployeeID
      FROM dbo.Orders
)

SELECT * 
  FROM OrdersRN
 WHERE RowNum BETWEEN (@PageNum - 1) * @PageSize + 1 
                  AND @PageNum * @PageSize
 ORDER BY OrderDate
         ,OrderID;

Другие советы

Я бы порекомендовал либо использовать LINQ, либо попытаться скопировать то, что он делает.У меня есть приложение, в котором я использую методы LINQ Take и Skip для получения постраничных данных.Код выглядит примерно так:

MyDataContext db = new MyDataContext();
var results = db.Products
    .Skip((pageNumber - 1) * pageSize)
    .Take(pageSize);

Запуск SQL Server Profiler показывает, что LINQ преобразует этот запрос в SQL аналогично:

SELECT [ProductId], [Name], [Cost], and so on...
FROM (
    SELECT [ProductId], [Name], [Cost], [ROW_NUMBER]
    FROM (
       SELECT ROW_NUMBER() OVER (ORDER BY [Name]) AS [ROW_NUMBER], 
           [ProductId], [Name], [Cost]
       FROM [Products]
    )
    WHERE [ROW_NUMBER] BETWEEN 10 AND 20
)
ORDER BY [ROW_NUMBER]

На простом английском языке:
1.Отфильтруйте строки и используйте функцию ROW_NUMBER, чтобы добавить номера строк в нужном порядке.
2.Фильтруйте (1), чтобы возвращать только те номера строк, которые вы хотите видеть на своей странице.
3.Сортируйте (2) по номеру строки, который соответствует желаемому порядку (в данном случае по имени).

По сути, существует два способа разбиения на страницы в базе данных (я предполагаю, что вы используете SQL Server):

Использование СМЕЩА

Другие объяснили, как 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 будет выполнять его более оптимально.

Использование метода ПОИСК

Существует совершенно другой, гораздо более быстрый, но менее известный способ выполнения подкачки в 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).

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

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

LINQ в сочетании с лямбда-выражениями и анонимными классами в .Net 3.5 чрезвычайно упрощает подобные вещи.

Запрос к базе данных:

var customers = from c in db.customers
                join p in db.purchases on c.CustomerID equals p.CustomerID
                where p.purchases > 5
                select c;

Количество записей на странице:

customers = customers.Skip(pageNum * pageSize).Take(pageSize);

Сортировка по любому столбцу:

customers = customers.OrderBy(c => c.LastName);

Получение только выбранных полей с сервера:

var customers = from c in db.customers
                join p in db.purchases on c.CustomerID equals p.CustomerID
                where p.purchases > 5
                select new
                {
                    CustomerID = c.CustomerID,
                    FirstName = c.FirstName,
                    LastName = c.LastName
                };

При этом создается статически типизированный анонимный класс, в котором вы можете получить доступ к его свойствам:

var firstCustomer = customer.First();
int id = firstCustomer.CustomerID;

Результаты запросов по умолчанию загружаются отложенно, поэтому вы не обращаетесь к базе данных до тех пор, пока вам действительно не понадобятся данные.LINQ в .Net также значительно упрощает обновления, сохраняя контекст данных всех внесенных вами изменений и обновляя только те поля, которые вы меняете.

Решение Oracle:

select * from (
    select a.*, rownum rnum from (
        YOUR_QUERY_GOES_HERE -- including the order by
    ) a
    where rownum <= MAX_ROW
 ) where rnum >= MIN_ROW

Есть несколько решений, которые я использую с MS SQL 2005.

Один из них — ROW_NUMBER().Но лично мне не нравится ROW_NUMBER(), потому что он не работает для больших результатов (БД, над которой я работаю, действительно большая - более 1 ТБ данных, выполняющих тысячи запросов в секунду - вы знаете - большие социальные сети сайт).

Вот мое любимое решение.

Я буду использовать своего рода псевдокод T-SQL.

Найдем 2-ю страницу пользователей, отсортированную по имени, фамилии, где на каждой странице по 10 записей.

@page = 2 -- input parameter
@size = 10 -- can be optional input parameter

if @page < 1 then begin
    @page = 1 -- check page number
end
@start = (@page-1) * @size + 1 -- @page starts at record no @start

-- find the beginning of page @page
SELECT TOP (@start)
    @forename = forename,
    @surname = surname
    @id = id
FROM
    users
ORDER BY
    forename,
    surname,
    id -- to keep correct order in case of have two John Smith.

-- select @size records starting from @start
SELECT TOP (@size)
    id,
    forename,
    surname
FROM
    users
WHERE
    (forename = @forename and surname = @surname and id >= @id) -- the same name and surname, but bigger id
    OR (forename = @forename and surname > @surname) -- the same name, but bigger surname, id doesn't matter
    OR (forename > @forename) -- bigger forename, the rest doesn't matter
ORDER BY
    forename,
    surname,
    id

На самом деле в LINQ есть методы Skip и Take, которые можно комбинировать, чтобы выбрать, какие записи следует выбирать.

Проверьте это.

Для БД: Пагинация в SQL Server 2005

Есть обсуждение по этому поводу Здесь

Этот метод получает страницу с номером 100 000 из базы данных на 150 000 строк за 78 мс.

Используя знания оптимизатора и команду SET ROWCOUNT, первый идентификатор сотрудника на запрошенной странице сохраняется в локальной переменной в качестве отправной точки.Затем установите для SET ROWCOUNT максимальное количество записей, запрошенное в @maximumRows.Это позволяет более эффективно разбивать по страницам набор результатов.При использовании этого метода также используются преимущества уже существующих индексов таблицы, поскольку он обращается непосредственно к базовой таблице, а не к локально созданной таблице.

Боюсь, я не могу судить, лучше ли это текущего принятого ответа.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top