168 votes

Enregistrement aléatoire dans ActiveRecord

Je suis dans le besoin d'obtenir un enregistrement aléatoire à partir d'une table via ActiveRecord. J'ai suivi l'exemple de Jamis Buck à partir de 2006.

Cependant, j'ai aussi trouver un autre moyen via une recherche Google (ne peut pas d'un attribut avec un lien grâce à de nouvelles restrictions de l'utilisateur):

 rand_id = rand(Model.count)
 rand_record = Model.first(:conditions => [ "id >= ?", rand_id])

Je suis curieux de voir comment d'autres ici l'ont fait ou si quelqu'un sait de quelle manière, ce serait plus efficace.

252voto

Mohamad Points 8497

Dans Rails 4 et Postgresql, vous pouvez effectuer les opérations suivantes:

Model.order("RANDOM()").first

Sans doute le même travail pour MySQL avec RAND()

Model.order("RAND()").first

143voto

Toby Hede Points 22128

Je n'ai pas trouvé un moyen idéal de le faire sans avoir au moins deux requêtes.

Celui-ci utilise un numéro généré de façon aléatoire (jusqu'à l'enregistrement en cours de comptage) comme un décalage.

offset = rand(Model.count)

# Rails 4
rand_record = Model.offset(offset).first

# Rails 3
rand_record = Model.first(:offset => offset)

Pour être honnête, je viens d'utiliser ORDER BY RAND() ou ALÉATOIRE() (selon la base de données). Ce n'est pas un problème de performances si vous n'avez pas de problème de performances.

74voto

semanticart Points 2773

Votre exemple de code va commencer à se comporter de façon inexacte une fois que les dossiers sont supprimés (il va favoriser injustement les articles avec le plus faible ids)

Vous êtes probablement mieux d'utiliser l'aléatoire des méthodes à l'intérieur de votre base de données. Celles-ci varient selon la DB que vous utilisez, mais :commande => "RAND()" fonctionne pour mysql et :commande => "RANDOM()" fonctionne pour postgres

Model.first(:order => "RANDOM()") # postgres example

29voto

dkam Points 1559

Analyse comparative de ces deux méthodes sur MySQL 5.1.49, Ruby 1.9.2p180 sur les produits de la table avec +5 milliers d'enregistrements:

def random1
  rand_id = rand(Product.count)
  rand_record = Product.first(:conditions => [ "id >= ?", rand_id])
end

def random2
  if (c = Product.count) != 0
    Product.find(:first, :offset =>rand(c))
  end
end

n = 10
Benchmark.bm(7) do |x|
  x.report("next id:") { n.times {|i| random1 } }
  x.report("offset:")  { n.times {|i| random2 } }
end


             user     system      total        real
next id:  0.040000   0.000000   0.040000 (  0.225149)
offset :  0.020000   0.000000   0.020000 ( 35.234383)

Décalage dans MySQL semble être beaucoup plus lent.

MODIFIER J'ai aussi essayé

Product.first(:order => "RAND()")

Mais j'ai dû le tuer après environ 60 secondes. MySQL a été "la Copie de tmp table sur le disque". Qui ne va pas au travail.

14voto

spilliton Points 1025

J'ai fait un rails 3 gem pour le gérer:

https://github.com/spilliton/randumb

Il vous permet de vous faire faire des trucs comme ça:

Model.where(:column => "value").random(10)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X