Разбиение на страницы быстро меняющегося содержимого базы данных

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

Если содержимое базы данных значительно меняется во время разбивки на страницы, простой запрос на разбивку на страницы, например:

SELECT *
FROM (SELECT a.*,
    ROW_NUMBER() OVER (ORDER BY id) AS rnum
    FROM articles a)
WHERE rnum <= 10
AND   rnum >= 6;

может работать не надежно. Вставленные строки могут быть пропущены или могут привести к повторению последующих строк, а удаленные строки могут привести к пропуску последующих строк.

Я мог бы избежать таких проблем, выполнив любое из следующих действий:

  1. Блокировка строк от обновления в течение всей разбивки на страницы — слишком строгие ограничения
  2. Скопируйте строки во временную таблицу перед пейджингом - слишком медленно
  3. Выбор по комбинации номера строки и отсортированного значения, которое отображалось в конце предыдущей страницы, возобновление в соответствующем месте на основе изменяющейся таблицы, но по-прежнему получение только следующих N строк

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

Например, предположим, что у меня есть список статей, отсортированных по убыванию рейтинга. Если рейтинг одинаковый, они сортируются по возрастанию идентификатора (идентификаторы уникальны):

ID      RATING
9       34
3       32
6       32
8       32
12      32
1       25
2       23

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

Теперь я хочу взять следующие 3 статьи, начиная со статьи 8, используя идентификатор статьи в качестве маркера, с которого можно продолжить.

Если бы я сказал базе данных взять репутацию статьи 8, а затем взять 3 статьи с репутацией ниже этой, я бы пропустил статью 12.

Если бы я сказал базе данных взять репутацию статьи 8, а затем взять 3 статьи, репутация которых ниже или равна этой, я бы повторил статьи 3 и 6.

Какой SQL-запрос (или комбинацию запросов) я могу использовать для возобновления разбиения на страницы из статьи 8, используя идентификатор статьи в качестве маркера для возобновления?


person Lorenzo Polidori    schedule 22.02.2012    source источник
comment
К сожалению, ваши требования не могут не противоречить сами себе. Ваша цель — всегда показывать людям статьи, которые они еще не видели в этом сеансе, или убедиться, что ранжирование является точным? Если я смотрю на первые 3, то кто-то меняет ID 8, чтобы иметь рейтинг 33 (опуская 6 на вторую страницу), вы не хотите показывать 6 на второй странице, потому что они уже видели это ? Что, если они снова перейдут на страницу 1?   -  person Aaron Bertrand    schedule 22.02.2012
comment
@AaronBertrand Хорошо, хорошая мысль. Я хочу показывать пользователям статьи, которые они раньше не видели. Что бы вы сделали тогда в этом случае? Является ли кэширование всей таблицы единственным решением для получения согласованных результатов на разных страницах?   -  person Lorenzo Polidori    schedule 22.02.2012
comment
Опять же, мне не ясна ваша цель. Вы хотите, чтобы пользователь просматривал устаревшие данные, даже если оценки за это время действительно изменились? Если бы я просматривал товары на eBay и ставка на один из них выросла, я бы хотел, чтобы он был точно пересортирован до того, как я сделаю ставку. Итак, какова реальная проблема, которую вы пытаетесь решить?   -  person Aaron Bertrand    schedule 22.02.2012
comment
@AaronBertrand В моем случае действительно важно, чтобы одна статья не повторялась в списке. Это связано с тем, что это часто отображается как прокручиваемый список, загружаемый страница за страницей, а не отдельные страницы, как в книге. Итак, в прокручивающемся списке было бы плохо увидеть статью дважды, а на отдельных страницах было бы не так уж и плохо. Кроме того, в этом случае приемлем статический снимок списка.   -  person Lorenzo Polidori    schedule 22.02.2012
comment
Тогда это звучит так, как будто вам нужно либо (а) кэшировать копию списка где-то в его исходной отсортированной форме, либо (б) прекратить упорядочивать данные, которые могут измениться во время прокрутки. Какую технологию вы используете, чтобы представить этот список конечному пользователю?   -  person Aaron Bertrand    schedule 22.02.2012
comment
@AaronBertrand Список запрашивается мобильным устройством в веб-службе, которая извлекает список из базы данных. Мобильное устройство отображает список в виде списка с прокруткой, который обновляется страница за страницей во время прокрутки. Отображение статей, упорядоченных по убыванию, является требованием приложения.   -  person Lorenzo Polidori    schedule 22.02.2012
comment
Таким образом, кажется, что вы должны кэшировать результат для этого пользователя (например, вы можете отправить все идентификаторы в приложение и заставить его просто извлекать 3 статьи за раз при каждой выборке), но также добавить заявление об отказе от ответственности что если им потребуется полчаса, чтобы прокрутить список, список может быть неточным.   -  person Aaron Bertrand    schedule 22.02.2012
comment
@AaronBertrand Большое спасибо. Если бы это был ответ, я бы его принял.   -  person Lorenzo Polidori    schedule 22.02.2012


Ответы (1)


Преобразование комментария в ответ, поскольку он, похоже, решил вопрос пользователя.

Таким образом, кажется, что вы должны кэшировать результат для этого пользователя (например, вы можете отправить все идентификаторы в приложение и заставить его просто извлекать 3 статьи за раз при каждой выборке), но также добавить отказ от ответственности, что если им потребуется половина час, чтобы просмотреть список, список может быть неточным.

person Aaron Bertrand    schedule 22.02.2012