2009-09-14 5 views
5

Ich habe eine aktuelle Implementierung von will_paginate, die die paginate_by_sql Methode verwendet, die Sammlung zu bauen paginierte werden. Wir haben eine benutzerdefinierte Abfrage für total_entries das ist sehr kompliziert und belastet unsere DB sehr. Deshalb möchten wir total_entries aus der Paginierung herausschneiden.Mit will_paginate ohne: total_entries eine lange Abfrage zu verbessern

Mit anderen Worten, anstelle der typischen Seitenumbruchanzeige von 'vorheriger 1 [2] 3 4 5 nächster' würden wir einfach nur einen 'nächsten - vorherigen' Knopf bevorzugen. Aber wir müssen ein paar Dinge wissen.

  1. Wird der vorherige Link angezeigt? Dies würde natürlich nur dann vorkommen, wenn Datensätze vor den in der aktuellen Auswahl angezeigten Daten vorhanden sind
  2. Zeigen wir den nächsten Link an? Dies wäre nicht angezeigt, wenn der letzte Datensatz in der Sammlung angezeigt wird

Vom docs

Eine Abfrage zum Zählen von Zeilen wird automatisch generiert werden, wenn Sie nicht liefern: total_entries. Wenn Sie Probleme mit dieser erzeugten SQL haben, möchten Sie möglicherweise führen Sie die Zählung manuell in Ihrer Anwendung.

So sind letztlich die ideale Situation ist die folgende.

  • entfernen die total_entries zählen, weil es zu viel Last auf die Datenbank verursacht
  • Anzeige 50 Datensätze gleichzeitig mit halb-Paginierung nur mit Vor/Zurück-Tasten, um zu navigieren und nicht alle Seitennummern anzuzeigen vorhanden
  • Nur die nächste Taste und vorherige Taste angezeigt wird entsprechend

Hat jemand arbeitet mit einem ähnlichen Problem oder haben Gedanken über eine Auflösung?

Antwort

13

Es gibt viele Gelegenheiten, wo will_paginate eine wirklich schreckliche Arbeit macht, die Anzahl der Einträge zu berechnen, vor allem, wenn es Joins gibt, die den count SQL Generator verwechseln.

Wenn Sie nur eine einfache prev/next-Methode benötigen, müssen Sie nur versuchen, N + 1 Einträge aus der Datenbank abzurufen, und wenn Sie nur N oder weniger erhalten als auf der letzten Seite .

Zum Beispiel:

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 

Sie können dies in einem gewissen Modul leicht einkapseln, die in den verschiedenen Modellen enthalten sein können, die sie benötigen, oder eine Controller-Erweiterung machen.

Ich habe festgestellt, dass dies deutlich besser als die Standardmethode will_paginate funktioniert.

Das einzige Leistungsproblem ist eine Einschränkung von MySQL, die abhängig von der Größe Ihrer Tabellen ein Problem sein kann.

Aus irgendeinem Grund ist die Zeit, die für eine Abfrage mit einem kleinen LIMIT in MySQL benötigt wird, proportional zum OFFSET. In der Tat liest das Datenbankmodul alle Zeilen, die zu dem bestimmten Offset-Wert führen, und gibt dann die nächsten Zeilen der LIMIT-Nummer zurück und springt nicht wie erwartet nach vorne.

Bei großen Datensätzen, deren OFFSET-Werte im Bereich von 100.000 plus liegen, kann es sein, dass die Leistung erheblich abnimmt. Wie sich dies manifestieren wird, ist das Laden von Seite 1 sehr schnell, Seite 1000 ist etwas langsam, aber Seite 2000 ist extrem langsam.

+0

Danke für die Idee. Ich werde hacken und sehen, was ich daraus machen kann. Ich habe ein paar langwierige SQL-Abfragen, mit denen ich mich befassen muss, aber ich denke, ich sollte in der Lage sein, sie in Lösung zu bringen und eine Vorstellung davon zu bekommen, wie die Leistung sein wird. Vielen Dank! – mwilliams

+0

Danke für die Antwort. Nett und einfach und ich sah weit über eine so einfache Lösung hinaus. Der Großteil meiner Implementierung ist vorhanden und scheint schon viel besser zu sein. Obwohl ich Probleme mit den nächsten/vorherigen Knöpfen habe, werde ich es bald genug herausgehämmert bekommen. Danke noch einmal! – mwilliams