Jak zdobyć ostatnie N rekordów z activerecord?

163

W :limitzapytaniu otrzymam pierwsze N ​​rekordów. Jaki jest najłatwiejszy sposób na zdobycie ostatnich N rekordów?

JtR
źródło

Odpowiedzi:

143

Wydaje mi się, że takie zapytanie dotyczące rekordu aktywnego dałoby ci to, czego chcesz („Coś” to nazwa modelu):

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

edycja : jak zauważono w komentarzach, inny sposób:

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

while !result.empty?
        puts result.pop
end
Dan McNevin
źródło
wydaje się zbędne zamawianie danych dwa razy, obecnie najpierw otrzymuję licznik i używam go z przesunięciem
JtR
To była inna metoda, o której myślałem, ale wydaje się, że jest to jeszcze więcej pracy, ponieważ są to 2 zapytania do bazy danych zamiast 1. Wydaje mi się, że w pewnym momencie musisz wykonać iterację po tablicy, więc możesz pozwolić na sortowanie ruby to w tamtym czasie. (tj. records.reverse.each do ..)
Dan McNevin
Alternatywnie, nie można odwrócić tablicy i użyć Array.pop do iteracji po tablicy zamiast jej odwracania. Ruby-doc.org/core-1.8.7/classes/Array.html#M000280
Dan
1
W przypadku Rails 3 zobacz zamiast tego odpowiedź Bonga. Ten sposób nadal działa, ale nie jest już preferowany.
Kyle Heironimus
3
Funkcja #find (: all) jest przestarzała. Zamiast tego zadzwoń bezpośrednio pod numer #all.
Snowcrash
262

To jest 3 sposób Railsów

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

SomeModel.last(5).reverse # last 5 records in descending order
Bongi
źródło
4
Spróbuj tego z Postgres! Z pewnością miałem problem z pierwszym. W SQL kolejność nie jest gwarantowana, chyba że ją określisz, ale MySQL jest bardziej wyrozumiały.
Ghoti,
5
Uwaga: nie jest to dobry sposób na jego implementację, jeśli chodzi o wydajność - przynajmniej nie do Railsów 3.1. SomeModel.last (5) wykona instrukcję select bez ograniczeń, zwracając wszystkie rekordy SomeModel do Ruby jako tablicę, po czym Ruby wybierze ostatnie (5) elementy. Jeśli chodzi o wydajność, obecnie chcesz użyć limitu - szczególnie jeśli masz potencjalnie wiele elementów.
DRobinson,
14
Robi dokładnie to, co powinien:SELECT "items".* FROM "items" ORDER BY id DESC LIMIT 50
firedev
7
W Railsach 4 zapytanie generowane przez .last () jest również optymalne, jak wspomniał Nick
Jimmie Tyrrell.
1
Jest to niepoprawne dla Rails 4+, ponieważ SomeModel.last(5).class== Array. Prawidłowe podejście jest SomeModel.limit(5).order('id desc')według Arthura Nevesa, co skutkujeSELECT \"somemodels\".* FROM \"somemodels\" ORDER BY id desc LIMIT 5
Amin Ariana
50

nowym sposobem na zrobienie tego w railsach 3.1 jest SomeModel.limit(5).order('id desc')

Arthur Neves
źródło
14
Jest to lepsze niż last(5)to, że zwraca zakres do dalszego łączenia.
lulalala
3
Używam SomeModel.limit(100).reverse_orderna Rails 4 ( guide.rubyonrails.org/ ... ) To to samo.
Ivan Black
Przykład „zakresu dalszego łączenia w łańcuch” wspomniany przez @lulalala: Jeśli potrzebujesz tylko jednej kolumny, SomeModel.limit(5).order('id desc').pluck(:col)zrobi SELECT SomeModel.col FROM SomeModel ORDER BY id desc LIMIT 5to znacznie wydajniej niż przechwycenie wszystkich kolumn i odrzucenie wszystkich oprócz jednej kolumny, z SomeModel.last(5).map(&:col) którą robi SELECT *zamiast SELECT col(nie możesz wyciągnąć po wywołaniu last; łańcuch lazy-eval kończy się na last).
Kanat Bolazar
33

Dla Rails 5 (i prawdopodobnie Rails 4)

Zły:

Something.last(5)

ponieważ:

Something.last(5).class
=> Array

więc:

Something.last(50000).count

prawdopodobnie zniszczy twoją pamięć lub potrwa wieczność.

Dobre podejscie:

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

ponieważ:

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"

Ten ostatni jest niedocenianym zakresem. Możesz go połączyć w łańcuch lub przekonwertować na tablicę za pośrednictwem .to_a. Więc:

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

... zajmuje chwilę.

Amin Ariana
źródło
1
A właściwie nawet Rails 3. ;)
Joshua Pinter
32

Dla wersji Rails 4 i nowszych:

Możesz spróbować czegoś takiego, jeśli chcesz mieć pierwszy najstarszy wpis

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

Możesz spróbować czegoś takiego, jeśli chcesz mieć najnowsze wpisy .

YourModel.order(id: :desc).limit(5).each do |d|
Gagan Gami
źródło
1
Wygląda na to, że odpowiedź na Rails 4 jest bardziej aktualna niż zaakceptowana. Myślę, że najnowsza składnia powinna wyglądać następująco:YourModel.all.order(id: :desc).limit(5)
jmarceli
@ user2041318: dzięki za nową składnię bez" "
Gagan Gami
2
Order () zwraca wszystkie rekordy. Nie ma potrzeby łączenia się w łańcuch, YourModel.all.order()ponieważ jest tak samo jakYourModel.order()
Francisco Quintero,
26

Rozwiązanie jest tutaj:

SomeModel.last(5).reverse

Ponieważ railsy są leniwe, w końcu trafią do bazy danych z kodem SQL, takim jak: "SELECT table. * FROM table ORDER BY table. idDESC LIMIT 5".

Deweloper
źródło
to załadowałoby wszystkie rekordySomeModel
John Hinnegan
Lepszym podejściem byłoby ograniczenie (); to nie wygląda na wydajne.
Anurag
3
@Developer ma całkowitą rację. Wykonany SQL to: SELECT tabela .* FROM tabela` ORDER BY table. idDESC LIMIT 5` nie wykonuje wyboru wszystkiego
ddavison
4

Jeśli chcesz ustawić kolejność wyników, użyj:

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

jeśli nie potrzebujesz żadnego zamówienia, a potrzebujesz tylko rekordów zapisanych w tabeli, użyj:

Model.last(n) # n= any number
Thorin
źródło
4

W moim (rails 4.2)projekcie szyn używam

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

i to działa.

timlentse
źródło
3

możemy użyć Model.last(5)lub Model.limit(5).order(id: :desc)w szynach 5.2

Joydeep Banerjee
źródło
2

Spróbuj:

Model.order("field_for_sort desc").limit(5)
Thorin
źródło
2
Powinieneś wyjaśnić, dlaczego to rozwiązanie jest wykonalne.
Phiter
1

Uważam, że to zapytanie jest lepsze / szybsze przy używaniu metody „pluck”, którą uwielbiam:

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

Daje to ActiveRecord jako wyjście; więc możesz użyć .pluck na tym w ten sposób:

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

który szybko podaje identyfikatory jako tablicę przy użyciu optymalnego kodu SQL.

Brandon Meredith
źródło
Challenge.limit(5).order('id desc').idsbędzie działać równie dobrze :)
Koen.
0

Jeśli masz domyślny zakres w swoim modelu, który określa kolejność rosnącą w Rails 3, będziesz musiał użyć zmiany kolejności zamiast kolejności określonej przez Arthura Nevesa powyżej:

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

lub

Something.reorder('id desc').limit(5)
scifisamurai
źródło
0

Powiedzmy, że N = 5, a Twój model to Message, możesz zrobić coś takiego:

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

Spójrz na sql:

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

Kluczem jest podselekcja. Najpierw musimy zdefiniować, jakie są ostatnie wiadomości, które chcemy, a następnie musimy uporządkować je w kolejności rosnącej.

MatayoshiMariano
źródło
-4

Dodaj parametr: order do zapytania

frankodwyer
źródło