`ORDER BY random()` is actually not very performant at the DB level unless
you have a small number of rows. This is because the database (at the very
least Postgres) must implement this with a full table scan, since there is
no other way to determine what all physical rows on disk are visible to the
query.

This is such a big problem that the SQL standard itself provides a special
way (rather than ordering by random) to sample tables. Postgres added
support for some of these features to 9.5. See
http://blog.2ndquadrant.com/tablesample-in-postgresql-9-5-2/

I'm not personally a fan of trying to abstract every database feature into
AR. I don't think it's at all plausible that any decent-sized application
that's doing anything interesting in the database will really be able to
use a drop-in-replacement database kind of mentality.

On Sat, Jul 23, 2016 at 1:11 PM, Vitaly Shvedchenko <
vitaly.shvedche...@gmail.com> wrote:

> 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.
>

-- 
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