Курсор на основе нумерации страниц? - PullRequest
0 голосов
/ 14 мая 2018

Для большого набора данных нумерация на основе offset становится медленной, и поэтому гораздо более быстрый способ - использовать пагинацию на основе курсора.По сути, это точка привязки, где база данных знает, как искать результаты с этого момента.Имея это в виду, вот проблема, с которой я сталкиваюсь:

У меня есть таблица tv_watchers с автоинкрементом id, mins_watching_tv и user_id (всего 20 строк скрипки ниже).В этом примере user_id будет таким же 1, поэтому не нужно беспокоиться об этом.Мы хотим отсортировать по количеству минут, потраченных на просмотр ТВ, от наивысшего к наименьшему.

Это легко сделать с помощью этого запроса:

SELECT * FROM tv_watchers
ORDER BY mins_watching_tv DESC, id ASC

Это вернет правильный порядок 20поля, упорядоченные таким образом, как мы хотим, по id:

2, 17, 1, 16, 15, 5, 6, 7, 8, 9, 10, 11, 12, 13, 20, 3, 4, 14, 19, 18

Проблема в том, что мы хотим разделить его на куски (мы называем их пакетами) по 5, поскольку мы хотим вернуть 5 результатов в порядкевыше.Мы делаем это, восстанавливая первые 6 результатов, возвращая первые 5 к пользователю, и с помощью 6-го, если он существует в качестве курсора (точки привязки), чтобы получить следующую партию из: Это возвращает первую партию правильно:

1019 *

6-й элемент здесь - это id 5, который имеет mins_watching_tv из 60, поэтому, поскольку это курсор, мы используем его, чтобы получить следующие 6 следующим образом:

-- (Batch 2) 5, 6, 7, 8, 9, 10
SELECT * FROM tv_watchers
WHERE mins_watching_tv <= 60 OR id=5
ORDER BY mins_watching_tv DESC, id ASC
LIMIT 6

6-й элемент здесь - это id 10, который также имеет mins_watching_tv из 60, поэтому, поскольку это курсор, мы используем его, чтобы получить следующие 6 следующим образом:

-- (Batch 3 should be) 10, 11, 12, 13, 20, 3
-- (Batch 3 returns incorrectly) 5, 6, 7, 8, 9, 10
SELECT * FROM tv_watchers
WHERE mins_watching_tv <= 60 OR id=10
ORDER BY mins_watching_tv DESC, id ASC
LIMIT 6

Нопроблема заключается в том, что возвращаемые результаты не являются правильными, возвращаются неверные 3 идентификатора партии, указанные в комментарии выше.Я уверен, что это связано с частью WHERE, кажется, что она забирает часть mins_watching_tv <= 60, но часть id=10 здесь, чтобы база данных знала, что нужно получить результаты с этой точки привязки 60 минут и идентификатора10, но это не работает правильно.

Окончательные результаты партии должны выглядеть следующим образом:

-- (Batch 4) 3, 4, 14, 19, 18

Я установил sql fiddle здесь , чтобы показать проблему,Как можно исправить запрос, чтобы он учитывал комбинацию курсоров mins_watching_tv в сочетании с id, чтобы возвращать правильные результаты в пакетах?

Ответы [ 2 ]

0 голосов
/ 14 мая 2018
  1. Выберите первые 6, как вы уже сделали, без чего-либо в WHERE.

    SELECT *
           FROM tv_watchers
           ORDER BY mins_watching_tv DESC,
                    id ASC
           LIMIT 6;
    
  2. Продолжительность @duration и идентификатор @id последней строки результата предыдущего шага и поместите их в WHERE как

    SELECT *
           FROM tv_watchers
           WHERE mins_watching_tv < @duration
                  OR mins_watching_tv = @duration
                     AND id >= @id
           ORDER BY mins_watching_tv DESC,
                    id ASC
           LIMIT 6;
    
  3. Повторяйте 2. пока не будет достигнут конец.

Объяснение:

  • Если mins_watching_tv < @duration, мы можем быть уверены, что соответствующая строка отсутствует в нашем предыдущем результате, так как mins_watching_tv меньше минимального @duration отнаш предыдущий результат, и мы сделали ORDER BY mins_watching_tv DESC.
  • Если mins_watching_tv = @duration, мы еще не знаем, если у нас уже была строка.Но поскольку мы дополнительно сделали ORDER BY id ASC, мы знаем, что все строки, которые у нас уже были с тем же mins_watching_tv, имеют идентификатор, меньший или равный текущему максимуму @id (на mins_watching_tv).Таким образом, мы хотим, чтобы только те строки, где id > @id или, как мы также хотим, чтобы последняя строка предыдущего результата повторилась, id = @id.Короче говоря, это id >= @id.

. Поскольку мы хотим объединить оба этих набора, мы должны разделить вышеуказанные предикаты, поэтому используйте OR.Мы получаем (скобки только для ясности, они не нужны):

(mins_watching_tv < @duration)
 OR (mins_watching_tv = @duration
     AND id >= @id)

И здесь - это скрипка.

0 голосов
/ 14 мая 2018

Я только скользил, но я думаю, что вам просто нужно настроить свои условия, чтобы (например)

mins_watching_tv < 60 OR (mins_watching_tv = 60 AND id>=5)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...