web-dev-qa-db-fra.com

Quel est le "Rails 4 Way" de trouver un certain nombre d'enregistrements aléatoires?

User.find(:all, :order => "RANDOM()", :limit => 10) était comme je l’ai fait dans Rails 3. 

User.all(:order => "RANDOM()", :limit => 10) est ainsi que je pensais que Rails 4 le ferait, mais cela me donne toujours un avertissement pour obsolescence:

DEPRECATION WARNING: Relation#all is deprecated. If you want to eager-load a relation, you can call #load (e.g. `Post.where(published: true).load`). If you want to get an array of records from a relation, you can call #to_a (e.g. `Post.where(published: true).to_a`).
37
justindao

Vous préférerez utiliser les méthodes order et limit. Vous pouvez vous débarrasser de la all.

Pour PostgreSQL et SQLite:

User.order("RANDOM()").limit(10)

Ou pour MySQL:

User.order("Rand()").limit(10)
94
Dylan Markow

Comme la fonction aléatoire pourrait changer pour différentes bases de données, je vous recommande d'utiliser le code suivant:

User.offset(Rand(User.count)).first

Bien sûr, cela n’est utile que si vous recherchez un seul enregistrement.

Si vous voulez en avoir plus d'un, vous pouvez faire quelque chose comme:

User.offset(Rand(User.count) - 10).limit(10)

Le - 10 vous assure d'obtenir 10 enregistrements au cas où Rand renvoie un nombre supérieur à compte - 10 .

Gardez à l'esprit que vous obtiendrez toujours 10 enregistrements consécutifs.

31
maurimiranda

Je pense que la meilleure solution consiste à commander de manière aléatoire dans la base de données . Mais si vous devez éviter une fonction aléatoire spécifique de la base de données, vous pouvez utiliser l'approche pluck et shuffle.

Pour un enregistrement:

User.find(User.pluck(:id).shuffle.first)

Pour plus d'un enregistrement:

User.where(id: User.pluck(:id).sample(10))
19
Prodis

Je suggèrerais d'en faire une portée car vous pouvez ensuite l'enchaîner:

class User < ActiveRecord::Base
  scope :random, -> { order(Arel::Nodes::NamedFunction.new('RANDOM', [])) }
end 

User.random.limit(10)
User.active.random.limit(10)
8
Kyle Decot

Pour MYSQL, cela a fonctionné pour moi:

User.order("Rand()").limit(10)
1
beesasoh

Recommandez vivement cette gemme pour les enregistrements aléatoires, spécialement conçue pour les tables avec beaucoup de lignes de données:

https://github.com/haopingfan/quick_random_records

Toutes les autres réponses fonctionnent mal avec une base de données volumineuse, à l'exception de cette gemme: 

  1. quick_random_records n'a coûté que 4.6ms totalement.

 enter image description here

  1. la réponse acceptée User.order('Rand()').limit(10) a coûté 733.0ms.

 enter image description here

  1. l'approche offset a coûté 245.4ms totalement.

 enter image description here

  1. l'approche User.all.sample(10) coût 573.4ms.

 enter image description here

Remarque: Ma table ne compte que 120 000 utilisateurs. Plus vous avez de disques, plus la différence de performance sera énorme.


METTRE À JOUR: 

Effectuer sur une table avec 550 000 lignes

  1. Model.where(id: Model.pluck(:id).sample(10)) coût 1384.0ms

 enter image description here

  1. gem: quick_random_records ne coûte que 6.4ms totalement

 enter image description here

1
Derek Fan

Vous pouvez appeler .sample sur les enregistrements, par exemple: User.all.sample(10)

0
s2t2