Como obter os últimos N registros com o activerecord?

163

Com a :limitconsulta, receberei os primeiros N registros. Qual é a maneira mais fácil de obter os últimos N registros?

JtR
fonte

Respostas:

143

Uma consulta de registro ativo como esta, acho que conseguiria o que você deseja ('Something' é o nome do modelo):

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

editar : Conforme observado nos comentários, de outra maneira:

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

while !result.empty?
        puts result.pop
end
Dan McNevin
fonte
parece desnecessário para pedir os dados duas vezes, Atualmente estou recebendo a primeira contagem e usá-lo com offset
JTR
Esse era o outro método em que eu estava pensando, mas parece ainda mais trabalhoso, já que são 2 consultas no banco de dados em vez de 1. Acho que presumo que você precise percorrer a matriz em algum momento, para permitir que o ruby ​​seja naquele momento. (. ie records.reverse.each fazer ..)
Dan McNevin
Como alternativa, você não poderia reverter a matriz e usar Array.pop para iterar a matriz em vez de inverter isso .. ruby-doc.org/core-1.8.7/classes/Array.html#M000280
Dan McNevin
1
Para o Rails 3, consulte a resposta dos Bongs. Dessa maneira, ainda funciona, mas não é mais a preferida.
Kyle Heironimus #
3
A chamada #find (: all) está obsoleta. Ligue para #all diretamente.
Snowcrash
262

Esta é a maneira Rails 3

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

SomeModel.last(5).reverse # last 5 records in descending order
Bongos
fonte
4
Tente isso com o Postgres! Eu certamente tive problemas com o primeiro. No SQL, a ordem não é garantida, a menos que você a especifique, mas o MySQL é mais tolerante.
Ghoti
5
Nota: esta não é uma boa maneira de implementá-lo, em termos de desempenho - pelo menos não até o Rails 3.1. SomeModel.last (5) executará a instrução select sem limite, retornando todos os registros de SomeModel para Ruby como uma matriz, após o qual Ruby selecionará os últimos (5) elementos. No que diz respeito à eficiência, atualmente, você deseja usar o limite - principalmente se você tiver potencialmente muitos elementos.
DRBinson
14
Ele faz exatamente o que deveria:SELECT "items".* FROM "items" ORDER BY id DESC LIMIT 50
firedev
7
Em Trilhos 4 a consulta gerada por .last () também é óptima como Nick mencionado
Jimmie Tyrrell
1
Isso está incorreto para o Rails 4+, pois SomeModel.last(5).class== Array. A abordagem correta é SomeModel.limit(5).order('id desc')por Arthur Neves, o que resulta emSELECT \"somemodels\".* FROM \"somemodels\" ORDER BY id desc LIMIT 5
Amin Ariana
50

nova maneira de fazê-lo no Rails 3.1 é SomeModel.limit(5).order('id desc')

Arthur Neves
fonte
14
Isso é melhor do que last(5)porque retorna um escopo para encadeamento adicional.
Lulalala
3
Eu uso SomeModel.limit(100).reverse_orderno Rails 4 ( Guides.rubyonrails.org/… ). É o mesmo.
Ivan Preto
Exemplo de "escopo para mais encadeamentos" mencionado por @lulalala: Se você precisar apenas de uma coluna, SomeModel.limit(5).order('id desc').pluck(:col)fará o SELECT SomeModel.col FROM SomeModel ORDER BY id desc LIMIT 5que é muito mais eficiente do que pegar todas as colunas e descartar todas, exceto uma coluna com a SomeModel.last(5).map(&:col) qual SELECT *substitui SELECT col(você não pode obter depois de chamar último; a cadeia de preguiçoso-final termina com o último).
Kanat Bolazar
33

Para Rails 5 (e provavelmente Rails 4)

Ruim:

Something.last(5)

Porque:

Something.last(5).class
=> Array

tão:

Something.last(50000).count

provavelmente explodirá sua memória ou levará uma eternidade.

Boa abordagem:

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

Porque:

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"

Este último é um escopo não avaliado. Você pode encadeá-lo ou convertê-lo em uma matriz via .to_a. Assim:

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

... leva um segundo.

Amin Ariana
fonte
1
E até o Rails 3, na verdade. ;)
Joshua Pinter
32

Para a versão Rails 4 e superior:

Você pode tentar algo assim: Se você deseja a primeira entrada mais antiga

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

Você pode tentar algo assim se quiser as últimas entradas mais recentes .

YourModel.order(id: :desc).limit(5).each do |d|
Gagan Gami
fonte
1
Parece uma resposta mais atualizada para o Rails 4 do que a aceita. Eu acho que a sintaxe mais recente deve ficar assim:YourModel.all.order(id: :desc).limit(5)
jmarceli
@ user2041318: obrigado por esta nova sintaxe sem" "
Gagan Gami
2
Order () retorna todos os registros. Não há necessidade de encadear, YourModel.all.order()porque é o mesmo queYourModel.order()
Francisco Quintero
26

A solução está aqui:

SomeModel.last(5).reverse

Como o rails é preguiçoso, ele eventualmente atingirá o banco de dados com SQL como: "SELECT table. * FROM table ORDER BY table. idDESC LIMIT 5".

Desenvolvedor
fonte
isso iria carregar todos os registros deSomeModel
John Hinnegan
Uma abordagem melhor seria limitar (); isso não parece eficiente.
Anurag
3
@ Developer está absolutamente certo. O SQL executado foi: SELECT table .* FROM table` ORDER BY table. idDESC LIMIT 5` não executa um select all
ddavison 23/11/2015
4

Se você precisar definir alguns pedidos para os resultados, use:

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

se você não precisa de nenhum pedido e precisa apenas de registros salvos na tabela, use:

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

No meu (rails 4.2)projeto rails , eu uso

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

e funciona.

timlentse
fonte
3

podemos usar Model.last(5)ou Model.limit(5).order(id: :desc)em trilhos 5.2

Joydeep Banerjee
fonte
2

Apenas tente:

Model.order("field_for_sort desc").limit(5)
Thorin
fonte
2
Você deve explicar por que essa solução é viável.
Phiter
1

Acho que essa consulta é melhor / mais rápida para usar o método "arrancar", que eu amo:

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

Isso fornece um ActiveRecord como a saída; então você pode usar o .pluck assim:

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

que fornece rapidamente os IDs como uma matriz enquanto usa o código SQL ideal.

Brandon Meredith
fonte
Challenge.limit(5).order('id desc').idsvai funcionar tão bem :)
Koen.
0

Se você tem um escopo padrão no seu modelo que especifica uma ordem crescente no Rails 3, precisará usar o reordenamento em vez da ordem, conforme especificado por Arthur Neves acima:

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

ou

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

Digamos N = 5 e seu modelo é Message, você pode fazer algo assim:

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

Veja o sql:

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

A chave é a subseleção. Primeiro, precisamos definir quais são as últimas mensagens que queremos e depois temos que ordená-las em ordem crescente.

MatayoshiMariano
fonte
-4

Adicione um parâmetro: order à consulta

frankodwyer
fonte