Rails 3:获取随机logging

所以,我发现了几个在Rails 2中find随机logging的例子 – 首选的方法似乎是:

Thing.find :first, :offset => rand(Thing.count) 

作为一个新手的东西,我不知道这是如何使用Rails 3中的新的查找语法来构build的。

那么,什么是“Rails 3 Way”来find随机logging呢?

 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,ruby1.9.3-P125-perf ),其中数据库是在本地主机和用户表有多个100K以上的logging

运用

由RAND()命令

很慢

User.order( “RAND(ID)”)。第一

SELECT users 。* FROM users ORDER BY RAND(id)限制1

并需要812秒的响应!

Rails日志:

用户负载(11030.8ms)selectusers * FROM users ORDER BY RAND()限制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 ),创build了一个临时表,并且需要一个额外的通道来获取所需的值( extra = Using temporary;使用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)SELECT users 。* FROM users WHERE usersid = 106854限制1

和MySQL的解释certificate了为什么:

 +----+-------------+-------+-------+---------------+--------------------------+---------+------+--------+-------------+ | 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在评论中指出的那样,如果表中有删除logging,上述解决scheme就有缺陷。

解决方法可以是

 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毫秒左右运行。

我做了一个rails 3 gem来完成这个工作,它在大型表上执行得更好,并允许你链接关系和范围:

https://github.com/spilliton/randumb

(编辑):我的gem的默认行为基本上使用上面相同的方法,但你可以select使用旧的方式,如果你想:)

如果使用Postgres

 User.limit(5).order("RANDOM()") 

如果使用MySQL

 User.limit(5).order("RAND()") 

在这两种情况下,您都要从Users表中随机select5条logging。 这是控制台中显示的实际SQL查询。

 SELECT * FROM users ORDER BY RANDOM() LIMIT 5 

开始了

轨道的方式

 #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:find一个随机logging 来源: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:查找多个随机logging(不重复) 来源:不记得
我需要find10个随机logging没有重复,所以这是我发现的工作
在你的控制器中:

 thing_ids = Thing.find( :all, :select => 'id' ).map( &:id ) @things = Thing.find( (1..10).map { thing_ids.delete_at( thing_ids.size * rand ) } ) 

这将会发现10个随机logging,但是值得一提的是,如果数据库特别大(数百万条logging),这将不是理想的,性能将受到阻碍。 是会执行好几千logging,这对我来说是足够的。

许多发布的答案在相当大的表格(100万行)上performance不佳。 随机sorting很快需要几秒钟,在桌面上进行计数也需要很长时间。

在这种情况下适合我的解决scheme是使用RANDOM()和where条件:

 Thing.where('RANDOM() >= 0.9').take 

在一个有超过一百万行的表上,这个查询通常less于2ms。

从列表中随机选取项目的Ruby方法是sample 。 为了创build一个有效的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) 

当然,你可以把这个问题放在这里 。

应用程序/模型/关注/ randomable.rb

 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 

然后…

应用程序/模型/ book.rb

 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/

如果使用Oracle

 User.limit(10).order("DBMS_RANDOM.VALUE") 

产量

 SELECT * FROM users ORDER BY DBMS_RANDOM.VALUE WHERE ROWNUM <= 10 

从表中获取多个随机logging的一种非常简单的方法。 这使得2便宜的查询。

Model.where(id: Model.pluck(:id).sample(3))

您可以将“3”更改为所需的随机logging数。

我刚刚遇到了这个问题,开发一个小应用程序,我想从我的数据库中select一个随机问题。 我用了:

 @question1 = Question.where(:lesson_id => params[:lesson_id]).shuffle[1] 

这对我来说很好。 由于这只是一个小应用程序,所以我不能说大型数据库的性能如何。