因此,我找到了一些在Rails 2中查找随机记录的示例-首选方法似乎是:
Thing.find :first, :offset => rand(Thing.count)
作为新手,我不确定如何在Rails 3中使用新的find语法来构造它。
那么,查找随机记录的“路轨3种方式”是什么?
因此,我找到了一些在Rails 2中查找随机记录的示例-首选方法似乎是:
Thing.find :first, :offset => rand(Thing.count)
作为新手,我不确定如何在Rails 3中使用新的find语法来构造它。
那么,查找随机记录的“路轨3种方式”是什么?
Answers:
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
对于大表,使用order 相当慢,但使用更多的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),该数据库位于localhost中,而users表中的记录多于100K。
使用
由RAND()订购
很慢
User.order(“ RAND(id)”)。first
变成
users
从users
RAND(id)LIMIT顺序中选择。*
并需要8到12秒才能做出回应!!
Rails日志:
用户负载(11030.8ms)
users
从users
RAND()限制中选择。* FROM 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)。
另一方面,通过将查询分为两部分并使用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限制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
转换为两个查询
SELECT COUNT(*) FROM `users`
SELECT `users`.* FROM `users` LIMIT 1 OFFSET 148794
并在大约500毫秒内运行。
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的默认行为基本上使用与现在相同的方法,但是如果需要的话,您可以选择使用旧方法:)
实际上,发布的许多答案在相当大的表(1+百万行)上表现不佳。随机排序很快就需要几秒钟,而对表进行计数也需要很长时间。
在这种情况下最适合我的解决方案是在RANDOM()
where条件下使用:
Thing.where('RANDOM() >= 0.9').take
在具有一百万行的表上,此查询通常花费不到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:找到一个随机记录源:特雷弗·
特克站点将其添加到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条随机记录,但是值得一提的是,如果数据库特别大(数百万条记录),这将不是理想的选择,并且性能会受到影响。Is将执行多达几千条对我来说足够的记录。
用于从列表中随机选择项目的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
将为调用数据库COUNT
。如果记录已被加载,这可能是个坏主意。重构将改为使用,#size
因为它将决定是否#count
应该使用,或者,如果记录已经加载,则使用#length
。
在Rails 5中运行,并且与数据库无关:
这在您的控制器中:
@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中,示例在Array中。api.rubyonrails.org/classes/Array.html#method-i-sample
强烈建议将此gem用于随机记录,它是专为具有大量数据行的表而设计的:
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”更改为所需的随机记录数。
我只是在开发一个小型应用程序时遇到了这个问题,我想从数据库中选择一个随机问题。我用了:
@question1 = Question.where(:lesson_id => params[:lesson_id]).shuffle[1]
而且对我来说很好。我不能说大型数据库的性能如何,因为这只是一个很小的应用程序。
shuffle[0]
)