133 votes

Rails 3: Obtenir un enregistrement aléatoire

Donc, j'ai trouvé plusieurs exemples pour trouver un enregistrement aléatoire dans Rails 2 - la méthode préférée semble être:

 Thing.find :first, :offset => rand(Thing.count)
 

En tant que débutant, je ne suis pas sûr de savoir comment cela pourrait être construit en utilisant la nouvelle syntaxe find dans Rails 3.

Alors, quel est le "Rails 3 Way" pour trouver un enregistrement aléatoire?

215voto

fl00r Points 41855
Thing.first(:order => "RANDOM()") # For MySQL :order => "RAND()", - thanx, @DanSingerman
# Rails 3
Thing.order("RANDOM()").first

ou

Thing.first(:offset => rand(Thing.count))
# Rails 3
Thing.offset(rand(Thing.count)).first

En fait dans Rails 3, tous les exemples de travail. Mais à l'aide de la commande RANDOM est assez lent pour les grandes tables, mais les plus de type sql

29voto

xlembouras Points 3809

Je suis en train de travailler sur un projet (Rails 3.0.15, ruby 1.9.3-p125-perf) où la db est en localhost et de la table des utilisateurs a un peu plus de 100 enregistrements.

À l'aide de

order by RAND()

est assez lent

De l'utilisateur.la commande("le RAND(id)").première

devient

SÉLECTIONNEZ users.* D' users ORDER BY RAND(id) LIMITE de 1

et prend de 8 à 12 secondes pour répondre!!

Rails de journal:

Charge de l'utilisateur (11030.8 ms) SÉLECTIONNEZ users.* D' users ORDER BY RAND() LIMITE de 1

à partir de mysql expliquer

+----+-------------+-------+------+---------------+------+---------+------+--------+---------------------------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows   | Extra                           |
+----+-------------+-------+------+---------------+------+---------+------+--------+---------------------------------+
|  1 | SIMPLE      | users | ALL  | NULL          | NULL | NULL    | NULL | 110165 | Using temporary; Using filesort |
+----+-------------+-------+------+---------------+------+---------+------+--------+---------------------------------+

Vous pouvez voir qu'aucun indice est utilisé (possible_keys = NULL), une table temporaire est créée et un supplément de passe est nécessaire pour récupérer la valeur souhaitée (extra = Aide temporaire; à l'Aide de filesort).

D'autre part, en divisant la requête en deux parties, et à l'aide de Ruby, nous avons raisonnable amélioration des temps de réponse.

users = User.scoped.select(:id);nil
User.find( users.first( Random.rand( users.length )).last )

(;néant pour la console)

Rails de journal:

Charge de l'utilisateur (25.2 ms) SELECT id from users de la Charge Utilisateur (de 0,2 ms) SÉLECTIONNEZ users.* D' usersusers.id = 106854 LIMITE de 1

et mysql expliquer prouve pourquoi:

+----+-------------+-------+-------+---------------+--------------------------+---------+------+--------+-------------+
| id | select_type | table | type  | possible_keys | key                      | key_len | ref  | rows   | Extra       |
+----+-------------+-------+-------+---------------+--------------------------+---------+------+--------+-------------+
|  1 | SIMPLE      | users | index | NULL          | index_users_on_user_type | 2       | NULL | 110165 | Using index |
+----+-------------+-------+-------+---------------+--------------------------+---------+------+--------+-------------+

+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref   | rows | Extra |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
|  1 | SIMPLE      | users | const | PRIMARY       | PRIMARY | 4       | const |    1 |       |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+

nous pouvons maintenant utiliser uniquement les index et les clés primaires et de faire le travail à environ 500 fois plus rapide!

11voto

spilliton Points 1025

J'ai fait un rails 3 gem pour cela que fonctionne mieux sur les grandes tables et vous permet d'enchaîner les relations et les étendues:

https://github.com/spilliton/randumb

(edit): Le comportement par défaut de mon gem utilise essentiellement la même démarche que ci-dessus maintenant, mais vous avez la possibilité d'utiliser l'ancienne méthode, si vous voulez :)

5voto

huan son Points 297

ici nous allons

les rails de chemin

#in your initializer
module ActiveRecord
  class Base
    def self.random
      if (c = count) != 0
        find(:first, :offset =>rand(c))
      end
    end
  end
end

l'utilisation de la

Model.random #returns single random object

ou la deuxième pensée est

module ActiveRecord
  class Base
    def self.random
      order("RAND()")
    end
  end
end

utilisation:

Model.random #returns shuffled collection

4voto

Hishalv Points 1954

C'était très utile pour moi en revanche j'ai besoin d'un peu plus de souplesse, c'est donc ce que j'ai fait:

Décision1: Trouver un enregistrement aléatoiresource:trevor turk site
Ajouter cette Chose.rb modèle

def self.random
    ids = connection.select_all("SELECT id FROM things")
    find(ids[rand(ids.length)]["id"].to_i) unless ids.blank?
end

puis dans votre contrôleur, vous pouvez appeler quelque chose comme ceci

@thing = Thing.random

Case2: Trouver de multiples enregistrements aléatoires(sans répétition)source:ne me souviens pas
J'ai besoin de trouver 10 enregistrements aléatoires sans se répète donc, c'est ce que j'ai trouvé travaillé
Dans votre contrôleur:

thing_ids = Thing.find( :all, :select => 'id' ).map( &:id )
@things = Thing.find( (1..10).map { thing_ids.delete_at( thing_ids.size * rand ) } )

Cela permettra de trouver 10 enregistrements aléatoires, mais ça vaut la peine de mentionner que si la base de données est particulièrement importante(en millions d'enregistrements), ce ne serait pas idéal, et le rendement sera entravée. Est fonctionnera bien jusqu'à quelques milliers de dossiers, qui était suffisant pour moi.

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