jyu*_*ood 147 random ruby-on-rails rails-activerecord
我需要通过ActiveRecord从表中获取随机记录.我从2006年开始跟随Jamis Buck的例子.
但是,我也通过Google搜索遇到了另一种方式(由于新的用户限制,无法使用链接进行归因):
rand_id = rand(Model.count)
rand_record = Model.first(:conditions => ["id >= ?", rand_id])
Run Code Online (Sandbox Code Playgroud)
我很好奇这里的其他人是如何做到的,或者是否有人知道哪种方式会更有效率.
Moh*_*mad 186
在Rails 4和5中,使用Postgresql或SQLite,使用RANDOM():
Model.order('RANDOM()').first
Run Code Online (Sandbox Code Playgroud)
想必同样会为工作的MySQL与RAND()
Model.order('RAND()').first
Run Code Online (Sandbox Code Playgroud)
警告:对于具有数百万条记录的大型数据集,这种情况很慢,因此您可能希望添加一个limit子句.
Tob*_*ede 130
如果没有至少两个查询,我还没有找到理想的方法.
以下使用随机生成的数字(最多为当前记录计数)作为偏移量.
offset = rand(Model.count)
# Rails 4
rand_record = Model.offset(offset).first
# Rails 3
rand_record = Model.first(:offset => offset)
Run Code Online (Sandbox Code Playgroud)
说实话,我刚刚使用ORDER BY RAND()或RANDOM()(取决于数据库).如果您没有性能问题,这不是性能问题.
sem*_*art 73
一旦删除记录,您的示例代码将开始表现不正确(它将不公平地支持具有较低ID的项目)
你最好在数据库中使用随机方法.这取决于哪个DB你使用的有所不同,但:为了=>"RAND()"的作品MySQL和:为了=>"RANDOM()"工程的Postgres
Model.first(:order => "RANDOM()") # postgres example
Run Code Online (Sandbox Code Playgroud)
dka*_*kam 29
在具有+ 500万条记录的产品表上对MySQL 5.1.49,Ruby 1.9.2p180上的这两种方法进行基准测试:
def random1
rand_id = rand(Product.count)
rand_record = Product.first(:conditions => [ "id >= ?", rand_id])
end
def random2
if (c = Product.count) != 0
Product.find(:first, :offset =>rand(c))
end
end
n = 10
Benchmark.bm(7) do |x|
x.report("next id:") { n.times {|i| random1 } }
x.report("offset:") { n.times {|i| random2 } }
end
user system total real
next id: 0.040000 0.000000 0.040000 ( 0.225149)
offset : 0.020000 0.000000 0.020000 ( 35.234383)
Run Code Online (Sandbox Code Playgroud)
MySQL中的偏移似乎要慢得多.
编辑 我也尝试过
Product.first(:order => "RAND()")
Run Code Online (Sandbox Code Playgroud)
但我必须在约60秒后杀死它.MySQL是"复制到磁盘上的tmp表".那不行.
Nie*_* B. 17
它不一定很难.
ids = Model.pluck(:id)
random_model = Model.find(ids.sample)
Run Code Online (Sandbox Code Playgroud)
pluck返回表中所有id的数组.在sample该阵列上的方法,则返回从所述阵列的随机ID.
这应该表现良好,具有相同的选择概率和支持已删除行的表.你甚至可以将它与约束混合在一起.
User.where(favorite_day: "Friday").pluck(:id)
Run Code Online (Sandbox Code Playgroud)
从而挑选一个喜欢星期五而不是任何用户的随机用户.
spi*_*ton 13
我做了一个rails 3 gem来处理这个问题:
https://github.com/spilliton/randumb
它允许你做这样的事情:
Model.where(:column => "value").random(10)
Run Code Online (Sandbox Code Playgroud)
Rya*_*lah 10
不建议您使用此解决方案,但如果由于某种原因您真的想在只进行一次数据库查询时随机选择记录,则可以使用Ruby Array类中的sample方法,该方法允许您选择随机项从一个数组.
Model.all.sample
Run Code Online (Sandbox Code Playgroud)
此方法仅需要数据库查询,但它比Model.offset(rand(Model.count)).first需要两个数据库查询的替代方法要慢得多,尽管后者仍然是首选.
我经常在控制台中使用它,我在初始化程序中扩展了ActiveRecord - Rails 4示例:
class ActiveRecord::Base
def self.random
self.limit(1).offset(rand(self.count)).first
end
end
Run Code Online (Sandbox Code Playgroud)
然后我可以打电话Foo.random给一个随机记录.
Postgres中的一个问题:
User.order('RANDOM()').limit(3).to_sql # Postgres example
=> "SELECT "users".* FROM "users" ORDER BY RANDOM() LIMIT 3"
Run Code Online (Sandbox Code Playgroud)
使用偏移量,两个查询:
offset = rand(User.count) # returns an integer between 0 and (User.count - 1)
Model.offset(offset).limit(1)
Run Code Online (Sandbox Code Playgroud)
强烈推荐这个 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 个用户。您拥有的记录越多,性能差异就越大。
阅读所有这些内容并没有让我对使用Rails 5和MySQL/Maria 5.5在特定情况下最适合哪些内容有很大的信心.所以我在约65000条记录上测试了一些答案,并且有两个收获:
limit是一个明显的赢家.pluck+ sample.def random1
Model.find(rand((Model.last.id + 1)))
end
def random2
Model.order("RAND()").limit(1)
end
def random3
Model.pluck(:id).sample
end
n = 100
Benchmark.bm(7) do |x|
x.report("find:") { n.times {|i| random1 } }
x.report("order:") { n.times {|i| random2 } }
x.report("pluck:") { n.times {|i| random3 } }
end
user system total real
find: 0.090000 0.000000 0.090000 ( 0.127585)
order: 0.000000 0.000000 0.000000 ( 0.002095)
pluck: 6.150000 0.000000 6.150000 ( 8.292074)
Run Code Online (Sandbox Code Playgroud)
这个答案综合,验证和更新了Mohamed的答案,以及Nami WANG对此的评论以及Florian Pilz对接受答案的评论 - 请向他们发送投票!
| 归档时间: |
|
| 查看次数: |
83129 次 |
| 最近记录: |