【发布时间】:2011-04-08 03:36:39
【问题描述】:
我不知道我是不是在这里看错了地方还是什么,但是活动记录有检索随机对象的方法吗?
类似的东西?
@user = User.random
或者......好吧,既然这种方法不存在,那么是否有一些惊人的“Rails 方式”可以做到这一点,我似乎总是很冗长。我也在用mysql。
【问题讨论】:
标签: mysql ruby-on-rails ruby
我不知道我是不是在这里看错了地方还是什么,但是活动记录有检索随机对象的方法吗?
类似的东西?
@user = User.random
或者......好吧,既然这种方法不存在,那么是否有一些惊人的“Rails 方式”可以做到这一点,我似乎总是很冗长。我也在用mysql。
【问题讨论】:
标签: mysql ruby-on-rails ruby
我见过的大多数这样做的示例最终都会计算表中的行数,然后生成一个随机数来选择一个。这是因为诸如RAND() 之类的替代方案效率低下,因为它们实际上获取每一行并为它们分配一个随机数,或者我已经阅读过(并且我认为是特定于数据库的)。
您可以添加一个类似我找到的方法 here。
module ActiveRecord
class Base
def self.random
if (c = count) != 0
find(:first, :offset =>rand(c))
end
end
end
end
这将使您使用的任何模型都有一个名为 random 的方法,该方法以我上面描述的方式工作:在表中的行数内生成一个随机数,然后获取与该随机数关联的行数字。所以基本上,你只做一次你可能更喜欢的 fetch :)
你也可以看看this rails plugin。
【讨论】:
offset(rand(c)).first
我们发现,对于一个大表,MySql 上的偏移量非常运行得很慢。而不是像这样使用偏移量:
model.find(:first, :offset =>rand(c))
...我们发现以下技术的运行速度提高了 10 倍以上(固定为 1):
max_id = Model.maximum("id")
min_id = Model.minimum("id")
id_range = max_id - min_id + 1
random_id = min_id + rand(id_range).to_i
Model.find(:first, :conditions => "id >= #{random_id}", :limit => 1, :order => "id")
【讨论】:
Model.where("id >= #{random_id}").first
尝试使用Array的sample方法:
@user = User.all.sample(1)
【讨论】:
User.order("RANDOM()").limit(10)(来自 stackoverflow.com/a/17373279/1298553)。
在 Rails 4 中我会扩展 ActiveRecord::Relation:
class ActiveRecord::Relation
def random
offset(rand(count))
end
end
这样你就可以使用作用域了:
SomeModel.all.random.first # Return one random record
SomeModel.some_scope.another_scope.random.first
【讨论】:
我会使用命名范围。只需将其放入您的用户模型中即可。
named_scope :random, :order=>'RAND()', :limit=>1
不过,每个数据库中的随机函数并不相同。 SQLite 和其他人使用RANDOM(),但你需要使用RAND() 来用于 MySQL。
如果您希望能够抓取多个随机行,您可以试试这个。
named_scope :random, lambda { |*args| { :order=>'RAND()', :limit=>args[0] || 1 } }
如果您调用 User.random,它将默认为 1,但如果您需要多个,也可以调用 User.random(3)。
【讨论】:
如果您需要随机记录但仅在某些条件下,您可以使用此代码中的“random_where”:
module ActiveRecord
class Base
def self.random
if (c = count) != 0
find(:first, :offset =>rand(c))
end
end
def self.random_where(*params)
if (c = where(*params).count) != 0
where(*params).find(:first, :offset =>rand(c))
end
end
end
end
例如:
@user = User.random_where("active = 1")
此功能对于根据一些附加条件显示随机产品非常有用
【讨论】:
User.random.where('active = 1') 而无需额外的全局方法。
User.where('active = 1').random,这会很好用......我不知道为什么我错过了:)
强烈推荐这个gem用于随机记录,它是专门为具有大量数据行的表设计的:
https://github.com/haopingfan/quick_random_records
简单用法:
@user = User.random_records(1).take
所有其他答案在大型数据库中表现不佳,除了这个 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 全部【讨论】:
这是从数据库中获取随机记录的最佳解决方案。 RoR 提供一切易用性。
要从数据库中获取随机记录,请使用sample,下面是示例说明。
基于 Marc-Andre Lafortune 的 github.com/marcandre/backports/ 的数组的反向移植#sample 从数组中返回一个随机元素或 n 个随机元素。如果数组为空且 n 为 nil,则返回 nil。如果传递了 n 并且其值小于 0,则会引发 ArgumentError 异常。如果 n 的值等于或大于 0,则返回 []。
[1,2,3,4,5,6].sample # => 4
[1,2,3,4,5,6].sample(3) # => [2, 4, 5]
[1,2,3,4,5,6].sample(-3) # => ArgumentError: negative array size
[].sample # => nil
[].sample(3) # => []
您可以根据您的要求使用条件,如下例所示。
User.where(active: true).sample(5)
它会从用户表中随机返回 5 个活跃用户
如需更多帮助,请访问:http://apidock.com/rails/Array/sample
【讨论】: