Hi, fellows!

Sometimes we need to get a random record from some table. The common way to 
get it is to use Array#sample method on a relation. Or, if your table is 
quiet large, you can use one of the ways your DB provides to do such thing. 
It could be 
MyModel.order('RAND()').limit(1)
if you use MySQL, or
MyModel.order('RANDOM()').limit(1)
if you use Postgresql.

This way is much more fast than using #sample method, even if your table is 
less then 1000 rows. But it has two downsides. 

First, this code is now database-dependent. So if your code will some day 
migrate on an another DB, you will have to change it. 
Second, it is not obvious for some rails novices, that doing 
`MyModel.all.sample` you actually use Array#sample method, that loads all 
relation collection in memory. If rails tend to be easy-to-start and clear 
for developer, it should not act this way. 

So, my suggestion is to implement #sample method somewhere in ActiveRecord 
so that it will generate database query to get random row. There are 
functions to do it in all common DB distributions (MySQL, Postgresql, MS 
SQL, IBM DB2, Oracle). The usage could look like this:

MyModel.sample # returns one random record
MyModel.sample(10) # returns 10 random records
MyModel.all.sample # works identical to the first two examples; and this is 
not the Array#sample method.
MyModel.some_scope.sample # returns one random record from a scope
and so on.

I think the performance benefit from such enhancement is quite obvious, but 
anyway, here is a benchmark for a Postgres table with 180 000 rows. 
2.1.5 (main):0 > Benchmark.bm do |x|
2.1.5 (main):0 *   x.report('sample') { LessonVersion.all.sample }
2.1.5 (main):0 *   x.report('RANDOM()') { 
LessonVersion.order('RANDOM()').limit(1) }
2.1.5 (main):0 * end
          user       system     total      real
sample     9.810000         8.690000        18.500000       ( 29.282437)
RANDOM()  0.000000   0.000000   0.000000   (0.000187)
=> [
  [0] #<Benchmark::Tms:0x007fe7c699c568 @label="sample", @real=29.282437, 
@cstime=0.0, @cutime=0.0, @stime=8.69, @utime=9.81, @total=18.5>,
  [1] #<Benchmark::Tms:0x007fe7c69a4420 @label="RANDOM()", @real=0.000187, 
@cstime=0.0, @cutime=0.0, @stime=0.0, @utime=0.0, @total=0.0>
]

I've already set up rails-dev-box on my laptop and cloned rails repository, 
but there is a CONTRIBUTING.md file, that recommends to get positive 
feedback before writing code. So, here is my proposal. I think I can write 
it myself, but ofcourse implementation suggestions are very welcome. 
Especially since I'm not very comfortible with ActiveRecord sources yet. 

-- 
You received this message because you are subscribed to the Google Groups "Ruby 
on Rails: Core" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to rubyonrails-core+unsubscr...@googlegroups.com.
To post to this group, send email to rubyonrails-core@googlegroups.com.
Visit this group at https://groups.google.com/group/rubyonrails-core.
For more options, visit https://groups.google.com/d/optout.

Reply via email to