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
。* FROMusers
ORDER BY RAND(id)限制1
并需要8至12秒的响应!
Rails日志:
用户负载(11030.8ms)select
users
* FROMusers
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)SELECTusers
。* FROMusers
WHEREusers
。id
= 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]
这对我来说很好。 由于这只是一个小应用程序,所以我不能说大型数据库的性能如何。