Wie bekomme ich die letzten N Datensätze mit Activerecord?

163

Mit :limitin Abfrage bekomme ich erste N Datensätze. Was ist der einfachste Weg, um die letzten N Datensätze zu erhalten?

JtR
quelle

Antworten:

143

Eine aktive Datensatzabfrage wie diese würde Ihnen meiner Meinung nach das bringen, was Sie wollen ('Etwas' ist der Modellname):

Something.find(:all, :order => "id desc", :limit => 5).reverse

bearbeiten : Wie in den Kommentaren erwähnt, eine andere Möglichkeit:

result = Something.find(:all, :order => "id desc", :limit => 5)

while !result.empty?
        puts result.pop
end
Dan McNevin
quelle
Es scheint unnötig, die Daten zweimal zu bestellen. Ich
erhalte
Das war die andere Methode, an die ich gedacht habe, aber das scheint noch mehr Arbeit zu sein, da dies 2 Abfragen für die Datenbank anstelle von 1 sind. Ich gehe davon aus, dass Sie irgendwann über das Array iterieren müssen, damit Sie Ruby sortieren können es zu dieser Zeit. (dh records.reverse.each do ..)
Dan McNevin
Alternativ können Sie das Array nicht umkehren und Array.pop verwenden, um über das Array zu iterieren, anstatt es umzukehren. Ruby-doc.org/core-1.8.7/classes/Array.html#M000280
Dan McNevin
1
Für Rails 3 siehe stattdessen die Antwort von Bongs. Dieser Weg funktioniert immer noch, ist aber nicht mehr der bevorzugte Weg.
Kyle Heironimus
3
Das Aufrufen von #find (: all) ist veraltet. Rufen Sie stattdessen #all direkt an.
Snowcrash
262

Dies ist der Rails 3-Weg

SomeModel.last(5) # last 5 records in ascending order

SomeModel.last(5).reverse # last 5 records in descending order
Bongs
quelle
4
Versuchen Sie dies mit Postgres! Ich hatte sicherlich zuerst Probleme damit. In SQL kann die Reihenfolge nur garantiert werden, wenn Sie sie angeben. MySQL ist jedoch fehlerverzeihender.
Ghoti
5
Hinweis: Dies ist keine gute Möglichkeit, es in Bezug auf die Leistung zu implementieren - zumindest nicht bis zu Rails 3.1. SomeModel.last (5) führt die select-Anweisung unbegrenzt aus und gibt alle Datensätze von SomeModel als Array an Ruby zurück. Danach wählt Ruby die letzten (5) Elemente aus. In Bezug auf die Effizienz möchten Sie derzeit Limit verwenden - insbesondere, wenn Sie möglicherweise viele Elemente haben.
DRobinson
14
Es macht genau das, was es sollte:SELECT "items".* FROM "items" ORDER BY id DESC LIMIT 50
Firedev
7
In Rails 4 ist die von .last () generierte Abfrage auch optimal, wie Nick erwähnt hat
Jimmie Tyrrell
1
Dies ist für Rails 4+ falsch, da SomeModel.last(5).class== Array. Der richtige Ansatz ist SomeModel.limit(5).order('id desc')per Arthur Neves, was zuSELECT \"somemodels\".* FROM \"somemodels\" ORDER BY id desc LIMIT 5
Amin Ariana
50

Neue Möglichkeit, dies in Schienen 3.1 zu tun, ist SomeModel.limit(5).order('id desc')

Arthur Neves
quelle
14
Dies ist besser als last(5)weil es einen Spielraum für weitere Verkettung zurückgibt.
Lulalala
3
Ich benutze SomeModel.limit(100).reverse_orderauf Rails 4 ( guides.rubyonrails.org/… ) Es ist das gleiche.
Ivan Black
Beispiel für „Spielraum für eine weitere Verkettung“ von @lulalala erwähnt: Wenn Sie nur eine Spalte benötigen, SomeModel.limit(5).order('id desc').pluck(:col)tun wird , SELECT SomeModel.col FROM SomeModel ORDER BY id desc LIMIT 5was viel effizienter ist , alle Spalten als Grabbing und alle bis auf eine Spalte mit Verwerfen SomeModel.last(5).map(&:col) denen tut SELECT *statt SELECT col(nicht zupfen kann nach dem Aufruf last; faul-eval Kette endet mit last).
Kanat Bolazar
33

Für Schienen 5 (und wahrscheinlich Schienen 4)

Schlecht:

Something.last(5)

weil:

Something.last(5).class
=> Array

so:

Something.last(50000).count

wird wahrscheinlich dein Gedächtnis in die Luft jagen oder ewig dauern.

Guter Ansatz:

Something.limit(5).order('id desc')

weil:

Something.limit(5).order('id desc').class
=> Image::ActiveRecord_Relation

Something.limit(5).order('id desc').to_sql
=> "SELECT  \"somethings\".* FROM \"somethings\" ORDER BY id desc LIMIT 5"

Letzteres ist ein nicht bewerteter Bereich. Sie können es verketten oder über in ein Array konvertieren .to_a. So:

Something.limit(50000).order('id desc').count

... dauert eine Sekunde.

Amin Ariana
quelle
1
Und sogar Rails 3. ;)
Joshua Pinter
32

Für Rails 4 und höher Version:

Sie können so etwas versuchen, wenn Sie den ersten ältesten Eintrag möchten

YourModel.order(id: :asc).limit(5).each do |d|

Sie können so etwas ausprobieren, wenn Sie die letzten Einträge wünschen .

YourModel.order(id: :desc).limit(5).each do |d|
Gagan Gami
quelle
1
Dies scheint eine aktuellere Antwort für Rails 4 zu sein als die akzeptierte. Ich denke, dass die neueste Syntax so aussehen sollte:YourModel.all.order(id: :desc).limit(5)
jmarceli
@ user2041318: Danke für diese neue Syntax ohne" "
Gagan Gami
2
Order () gibt alle Datensätze zurück. Es besteht keine Notwendigkeit zu verketten, YourModel.all.order()da es dasselbe ist wieYourModel.order()
Francisco Quintero
26

Lösung ist hier:

SomeModel.last(5).reverse

Da Rails faul ist, wird es schließlich mit SQL wie folgt in die Datenbank gelangen: "SELECT table. * FROM table ORDER BYtable . idDESC LIMIT 5".

Entwickler
quelle
Dies würde alle Aufzeichnungen vonSomeModel
John Hinnegan
Ein besserer Ansatz wäre es, () zu begrenzen; das sieht nicht effizient aus.
Anurag
3
@ Entwickler ist absolut richtig. Die ausgeführte SQL war: SELECT table .* FROM table` ORDER BY table. idDESC LIMIT 5` führt keine Auswahl aller aus
ddavison
4

Wenn Sie eine Reihenfolge für die Ergebnisse festlegen müssen, verwenden Sie:

Model.order('name desc').limit(n) # n= number

Wenn Sie keine Bestellung benötigen und nur in der Tabelle gespeicherte Datensätze benötigen, verwenden Sie:

Model.last(n) # n= any number
Thorin
quelle
4

In meinem Rails- (rails 4.2)Projekt verwende ich

Model.last(10) # get the last 10 record order by id

und es funktioniert.

timlentse
quelle
3

wir können Model.last(5)oder Model.limit(5).order(id: :desc)in Schienen 5.2 verwenden

Joydeep Banerjee
quelle
2

Probier's einfach:

Model.order("field_for_sort desc").limit(5)
Thorin
quelle
2
Sie sollten erklären, warum diese Lösung praktikabel ist.
Phiter
1

Ich finde, dass diese Abfrage besser / schneller für die Verwendung der "Zupf" -Methode ist, die ich liebe:

Challenge.limit(5).order('id desc')

Dies gibt einen ActiveRecord als Ausgabe; Sie können also .pluck wie folgt verwenden:

Challenge.limit(5).order('id desc').pluck(:id)

Dadurch werden die IDs schnell als Array angezeigt, während der optimale SQL-Code verwendet wird.

Brandon Meredith
quelle
Challenge.limit(5).order('id desc').idswird genauso gut funktionieren :)
Koen.
0

Wenn Sie in Ihrem Modell einen Standardbereich haben, der eine aufsteigende Reihenfolge in Rails 3 angibt, müssen Sie die Neuordnung anstelle der von Arthur Neves oben angegebenen Reihenfolge verwenden:

Something.limit(5).reorder('id desc')

oder

Something.reorder('id desc').limit(5)
Scifisamurai
quelle
0

Angenommen, N = 5 und Ihr Modell lautet Message: Sie können Folgendes tun:

Message.order(id: :asc).from(Message.all.order(id: :desc).limit(5), :messages)

Schauen Sie sich die SQL an:

SELECT "messages".* FROM (
  SELECT  "messages".* FROM "messages"  ORDER BY "messages"."created_at" DESC LIMIT 5
) messages  ORDER BY "messages"."created_at" ASC

Der Schlüssel ist die Unterauswahl. Zuerst müssen wir definieren, was die letzten Nachrichten sind, die wir wollen, und dann müssen wir sie in aufsteigender Reihenfolge bestellen.

MatayoshiMariano
quelle
-4

Fügen Sie der Abfrage einen: order-Parameter hinzu

frankodwyer
quelle