Случайная запись в ActiveRecord - PullRequest
144 голосов
/ 02 мая 2010

Мне нужно получить случайную запись из таблицы через ActiveRecord.Я следовал примеру из Jamis Buck из 2006 .

Однако я также натолкнулся на поиск в Google (не могу связать ссылку с новым пользователемограничения):

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

Мне любопытно, как другие здесь сделали это или кто-нибудь знает, какой путь будет более эффективным.

Ответы [ 24 ]

0 голосов
/ 27 апреля 2018

.order('RANDOM()').limit(limit) выглядит аккуратно, но медленно для больших таблиц, потому что ему нужно извлекать и сортировать все строки, даже если limit равен 1 (внутренне в базе данных, но не в Rails). Я не уверен насчет MySQL, но это происходит в Postgres. Больше объяснений в здесь и здесь .

Одним из решений для больших таблиц является .from("products TABLESAMPLE SYSTEM(0.5)"), где 0.5 означает 0.5%. Тем не менее, я считаю, что это решение все еще медленно, если у вас есть условия WHERE, которые отфильтровывают много строк. Я думаю, это потому, что TABLESAMPLE SYSTEM(0.5) извлекает все строки до применения WHERE условий.

Другое решение для больших таблиц (но не очень случайное):

products_scope.limit(sample_size).sample(limit)

, где sample_size может быть 100 (но не слишком большим, иначе он медленный и занимает много памяти), а limit может быть 1. Обратите внимание, что, хотя это быстро, но на самом деле не случайно, оно случайно только в sample_size записях.

PS: результаты тестов в ответах выше не являются надежными (по крайней мере, в Postgres), поскольку некоторые запросы к БД, выполняющиеся во 2-й раз, могут быть значительно быстрее, чем в 1-й раз, благодаря кешу БД. И, к сожалению, в Postgres нет простого способа отключить кеш, чтобы сделать эти тесты надежными.

0 голосов
/ 07 ноября 2013

Я новичок в RoR, но у меня это работает:

 def random
    @cards = Card.all.sort_by { rand }
 end

Это пришло от:

Как случайным образом отсортировать (перемешать) массив в Ruby?

0 голосов
/ 29 декабря 2017

Я пытаюсь использовать этот пример Сэма в своем приложении, используя rails 4.2.8 из Benchmark (я помещаю 1..Category.count для случайного числа, потому что если случайное значение принимает значение 0, это приведет к ошибке (ActiveRecord :: RecordNotFound: Couldn Категория с 'id' = 0)) и шахта была:

 def random1
2.4.1 :071?>   Category.find(rand(1..Category.count))
2.4.1 :072?>   end
 => :random1
2.4.1 :073 > def random2
2.4.1 :074?>    Category.offset(rand(1..Category.count))
2.4.1 :075?>   end
 => :random2
2.4.1 :076 > def random3
2.4.1 :077?>   Category.offset(rand(1..Category.count)).limit(rand(1..3))
2.4.1 :078?>   end
 => :random3
2.4.1 :079 > def random4
2.4.1 :080?>    Category.pluck(rand(1..Category.count))
2.4.1 :081?>
2.4.1 :082 >     end
 => :random4
2.4.1 :083 > n = 100
 => 100
2.4.1 :084 > Benchmark.bm(7) do |x|
2.4.1 :085 >     x.report("find") { n.times {|i| random1 } }
2.4.1 :086?>   x.report("offset") { n.times {|i| random2 } }
2.4.1 :087?>   x.report("offset_limit") { n.times {|i| random3 } }
2.4.1 :088?>   x.report("pluck") { n.times {|i| random4 } }
2.4.1 :089?>   end

                  user      system      total     real
find            0.070000   0.010000   0.080000 (0.118553)
offset          0.040000   0.010000   0.050000 (0.059276)
offset_limit    0.050000   0.000000   0.050000 (0.060849)
pluck           0.070000   0.020000   0.090000 (0.099065)
0 голосов
/ 12 октября 2017

Что делать:

rand_record = Model.find(Model.pluck(:id).sample)

Для меня все ясно

...