そのため、Rails 2でランダムなレコードを見つけるためのいくつかの例を見つけました-望ましい方法は次のようです:
Thing.find :first, :offset => Rand(Thing.count)
初心者であるため、Rails 3の新しい検索構文を使用してこれをどのように構築できるかわかりません。
それでは、ランダムなレコードを見つけるための「Rails 3 Way」とは何ですか?
Thing.first(:order => "RANDOM()") # For MySQL :order => "Rand()", - thanx, @DanSingerman
# Rails 3
Thing.order("RANDOM()").first
または
Thing.first(:offset => Rand(Thing.count))
# Rails 3
Thing.offset(Rand(Thing.count)).first
実際、Rails 3ではすべての例が機能します。しかし、順序RANDOM
を使用すると、大きなテーブルでは非常に遅くなりますが、より多くのsqlスタイル
UPD。インデックス付きカラムで次のトリックを使用できます(PostgreSQL構文):
select *
from my_table
where id >= trunc(
random() * (select max(id) from my_table) + 1
)
order by id
limit 1;
私はプロジェクトに取り組んでいます(Rails 3.0.15、Ruby 1.9.3-p125-perf)dbがlocalhostとusersテーブルにあります100Kレコードより少し多い。
を使用して
rand()による注文
かなり遅い
User.order( "Rand(id)")。first
になる
SELECT
users
。* FROMusers
ORDER BY Rand(id)LIMIT 1
8から12秒で応答します!!
Railsログ:
ユーザー負荷(11030.8ms)SELECT
users
。* FROMusers
ORDER BY Rand()LIMIT 1
mysqlの説明から
+----+-------------+-------+------+---------------+------+---------+------+--------+---------------------------------+
| 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 |
+----+-------------+-------+------+---------------+------+---------+------+--------+---------------------------------+
インデックスが使用されていないことがわかります(possible_keys = NULL)、一時テーブルが作成され、目的の値を取得するには追加のパスが必要です(extra = Using temporary; Using filesort)。
一方、クエリを2つの部分に分割し、Rubyを使用することで、応答時間を合理的に改善できます。
users = User.scoped.select(:id);nil
User.find( users.first( Random.Rand( users.length )).last )
(;コンソール使用の場合はなし)
Railsログ:
ユーザー負荷(25.2ms)SELECT id FROM
users
ユーザー負荷(0.2ms)SELECTusers
。* FROMusers
WHEREusers
..id
= 106854 LIMIT 1
mysqlのExplainはその理由を証明します:
+----+-------------+-------+-------+---------------+--------------------------+---------+------+--------+-------------+
| 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 | |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
インデックスと主キーのみを使用して、約500倍高速にジョブを実行できるようになりました。
更新:
コメントでicantbecoolが指摘したように、テーブルに削除されたレコードがある場合、上記のソリューションには欠陥があります。
その回避策は
users_count = User.count
User.scoped.limit(1).offset(Rand(users_count)).first
これは2つのクエリに変換されます
SELECT COUNT(*) FROM `users`
SELECT `users`.* FROM `users` LIMIT 1 OFFSET 148794
約500msで実行されます。
Postgresを使用している場合
User.limit(5).order("RANDOM()")
MySQLを使用している場合
User.limit(5).order("Rand()")
どちらの場合も、ユーザーテーブルから5つのレコードをランダムに選択しています。以下は、コンソールに表示される実際のSQLクエリです。
SELECT * FROM users ORDER BY RANDOM() LIMIT 5
これを行うためにRails 3 gemを作成しました。これは、大きなテーブルでパフォーマンスが向上し、リレーションとスコープを連鎖させることができます。
https://github.com/spilliton/randumb
(編集):私のgemのデフォルトの動作は、基本的に上記と同じアプローチを使用していますが、必要に応じて古い方法を使用するオプションがあります:)
実際に投稿された回答の多くは、かなり大きなテーブル(100万行以上)ではうまく機能しません。ランダムな順序付けにはすぐに数秒かかり、テーブルでのカウントも非常に長くかかります。
この状況でうまく機能する解決策は、RANDOM()
をwhere条件で使用することです:
Thing.where('RANDOM() >= 0.9').take
100万を超える行があるテーブルでは、このクエリの所要時間は通常2ミリ秒未満です。
さあ
#in your initializer
module ActiveRecord
class Base
def self.random
if (c = count) != 0
find(:first, :offset =>Rand(c))
end
end
end
end
Model.random #returns single random object
または二番目の考えは
module ActiveRecord
class Base
def self.random
order("Rand()")
end
end
end
使用法:
Model.random #returns shuffled collection
これは私にとって非常に便利でしたが、もう少し柔軟性が必要だったので、これが私がやったことです:
ケース1:ランダムなレコードを1つ見つけるソース:trevor turk site
これをThing.rbモデルに追加します
def self.random
ids = connection.select_all("SELECT id FROM things")
find(ids[Rand(ids.length)]["id"].to_i) unless ids.blank?
end
あなたのコントローラーでは、このようなものを呼び出すことができます
@thing = Thing.random
ケース2:複数のランダムレコードの検索(繰り返しなし)ソース:覚えていない
繰り返しのないランダムなレコードを10個見つける必要があったので、これがうまくいきました
コントローラー内:
thing_ids = Thing.find( :all, :select => 'id' ).map( &:id )
@things = Thing.find( (1..10).map { thing_ids.delete_at( thing_ids.size * Rand ) } )
これにより、ランダムなレコードが10個見つかりますが、データベースが特に大きい場合(数百万のレコード)、これは理想的ではなく、パフォーマンスが低下することに注意してください。私にとっては十分な数千件のレコードまで実行できます。
リストからアイテムをランダムに選択するRubyメソッドはsample
です。 ActiveRecordの効率的なsample
を作成したいので、以前の回答に基づいて、次を使用しました。
module ActiveRecord
class Base
def self.sample
offset(Rand(size)).first
end
end
end
これをlib/ext/sample.rb
に入れてから、これをconfig/initializers/monkey_patches.rb
にロードします:
Dir[Rails.root.join('lib/ext/*.rb')].each { |file| require file }
Rails 5で動作し、DBに依存しません:
これはあなたのコントローラーで:
@quotes = Quote.offset(Rand(Quote.count - 3)).limit(3)
もちろん、これを here のように懸念事項に入れることができます。
module Randomable
extend ActiveSupport::Concern
class_methods do
def random(the_count = 1)
records = offset(Rand(count - the_count)).limit(the_count)
the_count == 1 ? records.first : records
end
end
end
その後...
class Book < ActiveRecord::Base
include Randomable
end
次に、次の操作を行うだけで使用できます。
Books.random
または
Books.random(3)
ActiveRecordでsample()を使用できます
例えば。
def get_random_things_for_home_page
find(:all).sample(5)
end
ソース: http://thinkingeek.com/2011/07/04/easily-select-random-records-Rails/
ランダムレコードにこのgemを強くお勧めします。これは、大量のデータ行を持つテーブル用に特別に設計されています。
https://github.com/haopingfan/quick_random_records
このgemを除く他のすべての回答は、大規模なデータベースではパフォーマンスが低下します。
4.6ms
のみです。User.order('Rand()').limit(10)
cost 733.0ms
。offset
アプローチのコストは245.4ms
完全にかかります。User.all.sample(10)
アプローチコスト573.4ms
。注:私のテーブルには120,000人のユーザーしかいません。レコードが多いほど、パフォーマンスの差は大きくなります。
更新:
550,000行のテーブルで実行する
Model.where(id: Model.pluck(:id).sample(10))
コスト1384.0ms
gem: quick_random_records
のみのコスト6.4ms
完全にOracleを使用している場合
User.limit(10).order("DBMS_RANDOM.VALUE")
出力
SELECT * FROM users ORDER BY DBMS_RANDOM.VALUE WHERE ROWNUM <= 10