Rails 3:获取随机记录

时间:2011-03-17 16:41:24

标签: ruby-on-rails ruby ruby-on-rails-3 activerecord random

所以,我发现了几个在Rails 2中查找随机记录的例子 - 首选方法似乎是:

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

作为一个新手,我不确定如何使用Rails 3中的新查找语法构建它。

那么,找到随机记录的“Rails 3 Way”是什么?

14 个答案:

答案 0 :(得分:214)

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;

答案 1 :(得分:29)

我正在开发一个项目( Rails 3.0.15,ruby 1.9.3-p125-perf ),其中db位于 localhost 中,而users表有一点超过 100K记录

使用

  

按兰德()订购

很慢

  

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

变为

  

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

并从 8 12秒进行回复!!

Rails日志:

  

用户加载(11030.8ms)SELECT users。* 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 ),创建临时表并需要额外的传递来获取所需的值( extra = Using temporary; Using文件排序)。

另一方面,通过将查询拆分为两部分并使用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 LIMIT 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

并在大约500ms内运行。

答案 2 :(得分:12)

如果使用Postgres

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

如果使用MySQL

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

在这两种情况下,您都会从“用户”表中随机选择5条记录。以下是控制台中显示的实际SQL查询。

SELECT * FROM users ORDER BY RANDOM() LIMIT 5

答案 3 :(得分:11)

我做了一个rails 3 gem,这样做可以在大型表上表现更好,并允许你链接关系和范围:

https://github.com/spilliton/randumb

(编辑):我的gem的默认行为基本上使用与上面相同的方法,但是你可以选择使用旧方法:)

答案 4 :(得分:6)

实际上发布的许多答案在相当大的表(100多万行)上表现不佳。随机排序很快就需要几秒钟,而对表格进行计数也需要很长时间。

在这种情况下,对我来说效果很好的解决方案是将RANDOM()与where条件一起使用:

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

在一个行超过一百万的表上,此查询通常需要不到2毫秒。

答案 5 :(得分:5)

我们走了

rails way

#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

答案 6 :(得分:4)

这对我来说非常有用,但我需要更多的灵活性,所以这就是我所做的:

案例1:查找一个随机记录 来源:trevor turk site
将其添加到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个随机记录,但值得一提的是,如果数据库特别大(数百万条记录),这将不是理想的,性能将受到阻碍。对于我来说,它会表现出几千条记录。

答案 7 :(得分:4)

从列表中随机选择项目的Ruby方法是sample。想要为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 }

答案 8 :(得分:3)

适用于Rails 5并且与数据库无关:

这在您的控制器中:

@quotes = Quote.offset(rand(Quote.count - 3)).limit(3)

当然,您可以将其置于here所示的问题中。

的应用程序/模型/关切/ 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)

答案 9 :(得分:2)

您可以在ActiveRecord中使用sample()

E.g。

def get_random_things_for_home_page
  find(:all).sample(5)
end

来源:http://thinkingeek.com/2011/07/04/easily-select-random-records-rails/

答案 10 :(得分:1)

如果使用Oracle

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

输出

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

答案 11 :(得分:1)

强烈推荐这个gem用于随机记录,这是专为具有大量数据行的表而设计的:

https://github.com/haopingfan/quick_random_records

所有其他答案在大型数据库中都表现不佳,除了这个gem:

  1. quick_random_records仅花费4.6ms
  2. enter image description here

    1. 接受的答案User.order('RAND()').limit(10)费用为733.0ms
    2. enter image description here

      1. offset进近费用245.4ms
      2. enter image description here

        1. User.all.sample(10)进近费用573.4ms
        2. enter image description here

          注意:我的表只有120,000个用户。您拥有的记录越多,性能差异就越大。

          更新:

          在表格上执行550,000行

          1. Model.where(id: Model.pluck(:id).sample(10))费用1384.0ms
          2. enter image description here

            1. gem: quick_random_records仅花费6.4ms
            2. enter image description here

答案 12 :(得分:-2)

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

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

您可以将“3”更改为您想要的随机记录数。

答案 13 :(得分:-5)

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

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

它对我有用。我不能谈论大型数据库的性能如何,因为这只是一个小应用程序。