views:

416

answers:

3

I'm in need of getting a random record from a table via ActiveRecord. I've followed the example from Jamis Buck from 2006.

However, I've also come across another way via a Google search (can't attribute with a link due to new user restrictions):

 rand_id = rand(Model.count)
 rand_record = Model.first(:conditions => [ "id >= ?", rand_id])

I'm curious how others on here have done it or if anyone knows what way would be more efficient.

A: 

Your example code will start to behave inaccurately once records are deleted (it will unfairly favor items with lower ids)

You're probably better off using the random methods within your database. These vary depending on which DB you're using, but :order => "RAND()" works for mysql and :order => "RANDOM()" works for postgres

Model.first(:order => "RANDOM()") # postgres example
semanticart
ORDER BY RAND() for MySQL ends up in horrific runtime as data increases. It's unmaintainable (depending on time requirements) even starting at just thousands of rows.
Michael
Michael brings up a great point (that is true for other DBs as well). Generally selecting random rows from large tables isn't something you want to do in a dynamic action. Caching is your friend. Rethinking what you're trying to accomplish might not be a bad idea either.
semanticart
A: 

I'm actually using the code from Jamis Buck's example.

class Story < ActiveRecord::Base
  # random fuctionality snippet
  def self.find(*args)
    if args.first.to_s == "random"
      ids = connection.select_all("SELECT id FROM stories")
      super(ids[rand(ids.length)]["id"].to_i)
    else
      super
    end
  end
  # end of random snippet
end

Then access via:

Story.find(:random)

I'm just curious of other ways people have done this as there seems to be efficiency debates or db agnostic problems with most implementations.

jyu322
For large databases this would be incredibly slow.
Ryan Bigg
+3  A: 

I haven't found an ideal way to do this without at least two queries.

The following uses a randomly generated number (up to the current record count) as an offset.

offset = rand(Model.count)
rand_record = Model.first(:offset => offset)

To be honest, I've just been using ORDER BY RAND() or RANDOM() (depending on the database). It's not a performance issue if you don't have a performance issue.

Toby Hede
The code `Model.find(:offset => offset).first` will throw error. I think `Model.first(:offset => offset)` might perform better.
KandadaBoggu
yeah, i have been working with Rails 3 and keep getting confused about the query formats between versions.
Toby Hede