Использование will_paginate без: total_entries для улучшения длинного запроса - PullRequest
5 голосов
/ 14 сентября 2009

У меня есть текущая реализация will_paginate , которая использует метод paginate_by_sql для построения коллекции для разбивки на страницы. У нас есть специальный запрос для total_entries , который очень сложен и сильно загружает нашу БД. Поэтому мы хотели бы полностью исключить total_entries из нумерации страниц.

Другими словами, вместо обычного отображения нумерации страниц «предыдущий 1 [2] 3 4 5 следующий» нам просто нужна только кнопка «следующий - предыдущий». Но нам нужно знать несколько вещей.

  1. Отображаем ли мы предыдущую ссылку? Конечно, это произойдет только в том случае, если записи, существующие до записей, отображаемых в текущем выделении
  2. Отображаем ли мы следующую ссылку? Это не будет отображаться, если отображается последняя запись в коллекции

Из документов

Запрос на подсчет строк будет автоматически генерируется, если вы не поставлять: total_entries. если ты испытывают проблемы с этим сгенерированный SQL, вы можете захотеть выполнить подсчет вручную в вашем применение.

Итак, в конечном итоге идеальная ситуация следующая.

  • Удалить счетчик total_entries, так как он вызывает слишком большую нагрузку на базу данных
  • Отображение 50 записей за один раз с полупагинацией с использованием только следующих / предыдущих кнопок для навигации и без необходимости отображать все доступные номера страниц
  • Отображать только следующую кнопку и предыдущую кнопку соответственно

Кто-нибудь работал с подобной проблемой или есть мысли по поводу разрешения?

1 Ответ

14 голосов
/ 14 сентября 2009

Есть много случаев, когда will_paginate выполняет действительно ужасную работу по вычислению количества записей, особенно если в него включены объединения, которые приводят в замешательство генератор SQL count.

Если все, что вам нужно, это простой предыдущий / предыдущий метод, то все, что вам нужно сделать, это попытаться получить N + 1 записей из базы данных, и если вы получите только N или меньше, чем на последней странице.

Например:

per_page = 10
page = 2

@entries = Thing.with_some_scope.find(:all, :limit => per_page + 1, :offset => (page - 1) * per_page)

@next_page = @entries.slice!(per_page, 1)
@prev_page = page > 1

Вы можете легко инкапсулировать это в некоторый модуль, который может быть включен в различные модели, которые требуют этого, или сделать расширение контроллера.

Я обнаружил, что это работает значительно лучше, чем метод will_paginate по умолчанию.

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

По какой-то причине время, необходимое для выполнения запроса с небольшим LIMIT в MySQL, пропорционально OFFSET. По сути, ядро ​​базы данных считывает все строки, ведущие к конкретному значению смещения, а затем возвращает следующие строки числа LIMIT, не пропуская вперед, как вы ожидаете.

Для больших наборов данных, где значения OFFSET находятся в диапазоне от 100 000 с плюсом, производительность может значительно снизиться. Это проявится в том, что загрузка страницы 1 очень быстрая, страница 1000 несколько медленная, а страница 2000 очень медленная.

...