2009-09-14 7 views
5

j'ai une mise en œuvre actuelle de will_paginate qui utilise la méthode paginate_by_sql pour construire la collection à paginé. Nous avons une requête personnalisée pour total_entries c'est très compliqué et met une grande charge sur notre DB. Par conséquent, nous aimerions couper total_entries de la pagination complètement. En d 'autres termes, au lieu de l' affichage de pagination typique de 'précédent 1 [2] 3 4 5 suivant', nous aimerions simplement un bouton 'suivant - précédent' seulement. Mais nous devons savoir quelques choses.En utilisant will_paginate sans: total_entries pour améliorer une longue requête

  1. Est-ce que nous affichons le lien précédent? Cela ne se produirait bien sûr que si les enregistrements existaient avant ceux affichés dans la sélection en cours
  2. Montrons-nous le lien suivant? Ce ne serait pas affiché si le dernier enregistrement de la collection est affichée

De l'docs

Une requête pour les lignes de comptage sera généré automatiquement si vous ne fournissez pas : total_entries. Si vous rencontrez des problèmes avec ce généré SQL, vous voudrez peut-être effectuer le décompte manuellement dans votre application .

Donc finalement la situation idéale est la suivante.

  • Supprimer les total_entries comptent parce que cela cause trop de charge sur la base de données
  • Afficher 50 enregistrements à la fois avec demi-pagination en utilisant uniquement des boutons suivant/précédent pour naviguer et ne pas avoir besoin d'afficher tous les numéros de page disponibles
  • afficher uniquement le bouton suivant et le bouton précédent en conséquence

quelqu'un at-il travaillé avec un problème similaire ou avoir des pensées sur une résolution?

Répondre

13

Il y a beaucoup d'occasions où will_paginate fait un travail vraiment affreux de calculer le nombre d'entrées, particulièrement s'il y a des jointures impliquées qui confondent le générateur de compte SQL. Si vous n'avez besoin que d'une simple méthode prev/next, tout ce que vous avez à faire est d'essayer de récupérer N + 1 dans la base de données, et si vous n'obtenez que N ou moins que sur la dernière page .

Par exemple:

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 

Vous pouvez facilement résumer cela dans un certain module qui peut être inclus dans les différents modèles qui en ont besoin, ou faire une extension de contrôleur.

J'ai trouvé que cela fonctionne significativement mieux que la méthode par défaut will_paginate.

Le seul problème de performance est une limitation de MySQL qui peut poser problème selon la taille de vos tables.

Pour une raison quelconque, le temps nécessaire pour effectuer une requête avec une petite LIMITE dans MySQL est proportionnel à l'OFFSET. En effet, le moteur de base de données parcourt toutes les lignes menant à la valeur de décalage particulière, puis renvoie les lignes de nombre de LIMIT suivantes, ne sautant pas comme prévu.

Pour les ensembles de données volumineux, lorsque vous avez des valeurs OFFSET comprises entre 100 000 et plus, les performances peuvent être considérablement dégradées. Comment cela va se manifester est que le chargement de la page 1 est très rapide, la page 1000 est un peu lent, mais la page 2000 est extrêmement lente.

+0

Merci pour l'idée. Je vais me débrouiller et voir ce que je peux faire. J'ai de longues requêtes SQL personnalisées à traiter, mais je pense que je devrais être en mesure de les mettre en solution et d'avoir une idée de la performance. Merci! – mwilliams

+0

Merci pour votre réponse. Sympa et simpliste et j'ai regardé bien au-delà d'une solution aussi simple. La plupart de ma mise en œuvre est en place et semble déjà beaucoup mieux. Bien que je rencontre des problèmes avec les boutons suivants/précédents, mais je vais l'obtenir assez tôt. Merci encore! – mwilliams

Questions connexes