したがって、Rails 2でランダムなレコードを見つける例をいくつか見つけました。推奨される方法は次のようです。
Thing.find :first, :offset => rand(Thing.count)
初心者なので、Rails 3の新しい検索構文を使用してこれをどのように構築できるかわかりません。
では、ランダムなレコードを見つけるための「Rails 3 Way」とは何でしょうか。
したがって、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;
RAND()
またはRANDOM()
です。ありがとう
私はプロジェクトに取り組んでいます(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 =一時的な使用; filesortの使用)。
一方、クエリを2つの部分に分割し、Rubyを使用することで、応答時間を合理的に改善できます。
users = User.scoped.select(:id);nil
User.find( users.first( Random.rand( users.length )).last )
(;コンソール使用の場合はnil)
Railsログ:
ユーザーロード(25.2ms)SELECT id FROM
users
ユーザーロード(0.2ms)SELECTusers
。* FROMusers
WHEREusers
。id
= 106854制限1
そしてmysqlの説明は理由を証明します:
+----+-------------+-------+-------+---------------+--------------------------+---------+------+--------+-------------+
| 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で実行されます。
RAND(id)
はありません。RAND()
クエリごとに異なる順序が必要な場合に使用します。
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ミリ秒未満です。
take
を提供LIMIT(1)
するが配列ではなく単一の要素を返す関数を使用することです。したがって、呼び出す必要はありませんfirst
さあ行こう
#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
Couldn't find all Users with 'id': (first, {:offset=>1}) (found 0 results, but was looking for 2)
"RANDOM()"
代わりに使用できます...
これは私にとって非常に役に立ちましたが、もう少し柔軟性が必要だったので、これは私がやったことです:
ケース1:ランダムなレコードソースを1つ見つける:Trevor turkサイト
これを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
です。sample
ActiveRecordの効率を作成するために、以前の回答に基づいて、私は以下を使用しました:
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 }
#count
は、のDBを呼び出しますCOUNT
。レコードが既にロードされている場合、これは悪い考えかもしれません。使用#size
するかどうか#count
、またはレコードがすでにロードされている場合はを使用するかどうかを決定するため、代わりにリファクタリングを使用します#length
。
Rails 5で動作し、DBに依存しません。
これはあなたのコントローラーで:
@quotes = Quote.offset(rand(Quote.count - 3)).limit(3)
もちろん、ここに示すように、これを考慮に入れることができます。
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/
sample
ActiveRecordにはなく、サンプルは配列にあります。api.rubyonrails.org/classes/Array.html#method-i-sample
Oracleを使用している場合
User.limit(10).order("DBMS_RANDOM.VALUE")
出力
SELECT * FROM users ORDER BY DBMS_RANDOM.VALUE WHERE ROWNUM <= 10
ランダムレコードには、この宝石を強くお勧めします。ランダムレコードは、大量のデータ行を持つテーブル用に特別に設計されています。
https://github.com/haopingfan/quick_random_records
他のすべての回答は、このgemを除いて、大規模なデータベースではうまく機能しません。
4.6ms
全体のコストのみです。User.order('RAND()').limit(10)
費用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
完全に費用のみテーブルから複数のランダムなレコードを取得する非常に簡単な方法。これにより、2つの安価なクエリが作成されます。
Model.where(id: Model.pluck(:id).sample(3))
「3」は、必要なランダムレコードの数に変更できます。
私はこの問題に遭遇し、自分のDBからランダムな質問を選択したい小さなアプリケーションを開発しました。私が使用した:
@question1 = Question.where(:lesson_id => params[:lesson_id]).shuffle[1]
そしてそれは私にとってはうまく機能しています。これは小さなアプリケーションなので、大きなDBのパフォーマンスについては説明できません。
shuffle[0]
)