Comment obtenir les N derniers enregistrements avec activerecord?

163

Avec :limiten requête, j'obtiendrai N premiers enregistrements. Quelle est la manière la plus simple d'obtenir les N derniers enregistrements?

JtR
la source

Réponses:

143

Une requête d'enregistrement active comme celle-ci, je pense, vous permettrait d'obtenir ce que vous voulez (`` Quelque chose '' est le nom du modèle):

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

edit : Comme indiqué dans les commentaires, une autre façon:

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

while !result.empty?
        puts result.pop
end
Dan McNevin
la source
semble inutile de commander les données deux fois,
j'obtiens
C'était l'autre méthode à laquelle je pensais, mais cela semble encore plus de travail puisque c'est 2 requêtes contre la base de données au lieu de 1. Je suppose que je suppose que vous devez itérer sur le tableau à un moment donné, afin que vous puissiez laisser ruby ​​trier à ce moment-là. (ie. records.reverse.each do ..)
Dan McNevin
Sinon, vous ne pouvez pas inverser le tableau et utiliser Array.pop pour parcourir le tableau plutôt que de l'inverser .. ruby-doc.org/core-1.8.7/classes/Array.html#M000280
Dan McNevin
1
Pour Rails 3, voyez plutôt la réponse de Bongs. Cette méthode fonctionne toujours, mais ce n'est plus la méthode préférée.
Kyle Heironimus
3
L'appel de #find (: all) est obsolète. Appelez #all directement à la place.
Snowcrash
262

C'est la voie 3 Rails

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

SomeModel.last(5).reverse # last 5 records in descending order
Bangs
la source
4
Essayez ceci avec Postgres! J'ai certainement eu des problèmes avec le premier. En SQL, l'ordre n'est garanti que si vous le spécifiez, mais MySQL est plus indulgent.
Ghoti
5
Remarque: ce n'est pas une bonne façon de l'implémenter, du point de vue des performances - du moins pas à la hauteur de Rails 3.1. SomeModel.last (5) exécutera l'instruction select sans limite, renvoyant tous les enregistrements de SomeModel à Ruby sous forme de tableau, après quoi Ruby sélectionnera les (5) derniers éléments. En termes d'efficacité, vous souhaitez actuellement utiliser la limite - en particulier si vous avez potentiellement de nombreux éléments.
DRobinson
14
Il fait exactement ce qu'il devrait:SELECT "items".* FROM "items" ORDER BY id DESC LIMIT 50
firedev
7
Dans Rails 4, la requête générée par .last () est également optimale comme Nick l'a mentionné
Jimmie Tyrrell
1
Ceci est incorrect pour Rails 4+, puisque SomeModel.last(5).class== Array. L'approche correcte est SomeModel.limit(5).order('id desc')par Arthur Neves, ce qui se traduit parSELECT \"somemodels\".* FROM \"somemodels\" ORDER BY id desc LIMIT 5
Amin Ariana
50

la nouvelle façon de le faire dans les rails 3.1 est SomeModel.limit(5).order('id desc')

Arthur Neves
la source
14
C'est mieux que last(5)parce que cela renvoie une possibilité de chaînage supplémentaire.
lulalala du
3
J'utilise SomeModel.limit(100).reverse_ordersur Rails 4 ( guides.rubyonrails.org / ... ) C'est pareil.
Ivan Black
Exemple de "portée pour un chaînage supplémentaire" mentionné par @lulalala: Si vous n'avez besoin que d'une seule colonne, SomeModel.limit(5).order('id desc').pluck(:col)fera ce SELECT SomeModel.col FROM SomeModel ORDER BY id desc LIMIT 5qui est beaucoup plus efficace que de saisir toutes les colonnes et de supprimer toutes les colonnes sauf une avec SomeModel.last(5).map(&:col) qui fait SELECT *au lieu de SELECT col(vous ne pouvez pas cueillir après avoir appelé last; la chaîne lazy-eval se termine par le dernier).
Kanat Bolazar
33

Pour les rails 5 (et probablement les rails 4)

Mauvais:

Something.last(5)

car:

Something.last(5).class
=> Array

alors:

Something.last(50000).count

va probablement exploser votre mémoire ou prendre une éternité.

Bonne approche:

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

car:

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"

Ce dernier est un périmètre non évalué. Vous pouvez le chaîner ou le convertir en tableau via .to_a. Alors:

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

... prend une seconde.

Amin Ariana
la source
1
Et même Rails 3, en fait. ;)
Joshua Pinter
32

Pour Rails 4 et version supérieure:

Vous pouvez essayer quelque chose comme ceci Si vous voulez la première entrée la plus ancienne

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

Vous pouvez essayer quelque chose comme ceci si vous voulez les dernières dernières entrées .

YourModel.order(id: :desc).limit(5).each do |d|
Gagan Gami
la source
1
Cela ressemble à une réponse plus à jour pour Rails 4 que celle acceptée. Je pense que la syntaxe la plus récente devrait ressembler à ceci:YourModel.all.order(id: :desc).limit(5)
jmarceli
@ user2041318: merci pour cette nouvelle syntaxe sans" "
Gagan Gami
2
Order () renvoie tous les enregistrements. Il n'est pas nécessaire de chaîner YourModel.all.order()car c'est le même queYourModel.order()
Francisco Quintero
26

La solution est ici:

SomeModel.last(5).reverse

Puisque rails est paresseux, il finira par atteindre la base de données avec SQL comme: "SELECT table. * FROM table ORDER BY table. idDESC LIMIT 5".

Développeur
la source
cela chargerait tous les enregistrements deSomeModel
John Hinnegan
Une meilleure approche serait de limiter (); cela n'a pas l'air efficace.
Anurag
3
@Developer a tout à fait raison. Le SQL exécuté était: SELECT table .* FROM table` ORDER BY table. idDESC LIMIT 5` il n'effectue pas de sélection de tout
ddavison
4

Si vous avez besoin de définir un ordre sur les résultats, utilisez:

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

si vous n'avez besoin d'aucune commande et que vous avez juste besoin d'enregistrements enregistrés dans la table, utilisez:

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

Dans mon (rails 4.2)projet de rails , j'utilise

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

et il fonctionne.

Timlentse
la source
3

on peut utiliser Model.last(5)ou Model.limit(5).order(id: :desc)en rails 5.2

Joydeep Banerjee
la source
2

Essayez simplement:

Model.order("field_for_sort desc").limit(5)
Thorin
la source
2
Vous devez expliquer pourquoi cette solution est viable.
Phiter
1

Je trouve que cette requête est meilleure / plus rapide pour utiliser la méthode "pluck", que j'adore:

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

Cela donne un ActiveRecord comme sortie; vous pouvez donc utiliser .pluck dessus comme ceci:

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

qui donne rapidement les identifiants sous forme de tableau tout en utilisant un code SQL optimal.

Brandon Meredith
la source
Challenge.limit(5).order('id desc').idsfonctionnera tout aussi bien :)
Koen.
0

Si vous avez une portée par défaut dans votre modèle qui spécifie un ordre croissant dans Rails 3, vous devrez utiliser la réorganisation plutôt que l'ordre comme spécifié par Arthur Neves ci-dessus:

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

ou

Something.reorder('id desc').limit(5)
scifisamurai
la source
0

Disons que N = 5 et votre modèle est Message, vous pouvez faire quelque chose comme ceci:

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

Regardez le sql:

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

La clé est la sous-sélection. Nous devons d'abord définir quels sont les derniers messages que nous voulons, puis nous devons les ordonner par ordre croissant.

MatayoshiMariano
la source
-4

Ajouter un paramètre: order à la requête

Frankodwyer
la source