Random Records in Rails
December 4th, 2007 by TrevorThere are a number of different ways to retrieve random items from a database in Rails, most of which have been discussed at length on the Rails wiki. According to this article, there are 3 preferred methods:
- 1. Select a Record by Random Offset
- 2. Randomize with the Database
- 3. Randomize with Ruby
For an in-depth look into these options, you can peruse this discussion page, which details some of the pros and cons of different strategies for randomization.
After trying out a few of the techniques that abound in this area, I stumbled across an article from Jamis Buck, where he discusses a RESTful way to approach the creation of custom finders. Although randomization isn't the focus of the article, he does provide a bit of guidance in that regard. His strategy uses Ruby for the randomization, and employs 2 light-weight queries. The first query gathers a list of valid ids. The second simply selects a single item using that (randomized) id.
So, I did a bit of cargo-culting and repurposing to achieve an efficient, database agnostic way to retrieve random items from a database using ActiveRecord. Simply add the following to the model from which you'd like to be able to pull random records:
class Widget < ActiveRecord::Base # ... def self.random ids = connection.select_all("SELECT id FROM widgets") find(ids[rand(ids.length)]["id"].to_i) unless ids.blank? end end
Then, you can use the following bit of code in a controller like so:
class SomeController < ApplicationController # ... def some_action @widget = Widget.random end end
And you've got a small and efficient "random finder" for use throughout your app. Lovely.

It could be me, but isn’t retrieving _all_ ID’s from the database and only using one the opposite of efficient?
I have no clue how to do this in a more efficient way, but that’s just my 0.02$ :)
I’ve heard that this may not be the most efficient way if you have lot of data, but it seems to work well enough for me, and it’s database agnostic, which I like.
That’s definitely not going to fly for large data sets.
I’ve heard that before, but I have yet to see a better solution :)
Not to add to the fracas (I agree large sets could be unwieldy), but this is a little more agnostic:
def self.random_50
all_ids = find(:all, :select => :id)
random_ids = 50.times.collect{|t| rand(all_ids.length)}
find(random_ids)
end
You could end up with repeats, but so can your method.
Opps, sorry, copied an older version. This is much better:
def self.random_50
all_ids =find( :all, :select => :id )
random_ids = 50.times.collect{ |t| all_ids[rand(all_ids.length)].id }
find(random_ids)
end