2009-09-14 3 views
5

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

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

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

От docs

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

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

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

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

ответ

13

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

Если все, что вам нужно, это простой метод prev/next, то все, что вам нужно сделать, это попытаться извлечь 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 очень медленная.

+0

Спасибо за идею. Я собираюсь взломать и посмотреть, что я могу придумать. У меня есть некоторые длинные пользовательские SQL-запросы для решения, но я думаю, что я должен был бы заставить их работать в решении и получить представление о том, как будет работать. Благодаря! – mwilliams

+0

Спасибо за ответ. Ницца и упрощение, и я смотрел за пределы такого простого решения. Большая часть моей реализации уже создана и кажется намного лучше. Хотя у меня проблемы со следующими/предыдущими кнопками, но я скоро заработаю. Еще раз спасибо! – mwilliams