2015-03-04 3 views
0

я делаю что-то вроде этого:Rails - как получить случайные записи из объекта?

data = Model.where('something="something"') 
random_data = data.rand(100..200) 

возвращается:

NoMethodError (private method `rand' called for #<User::ActiveRecord_Relation:0x007fbab27d7ea8>): 

После того, как я получаю эти случайные данные, которые мне нужно перебирать эти данные, как это:

random_data.each do |rd| 
    ... 

Я знаю, что есть способ получить случайные данные в MySQL, но мне нужно выбрать случайные данные, например, 400 раз, поэтому я думаю, что однажды загружать данные из базы данных и 400 раз, чтобы выбрать случайное число, более эффективный, чем запуск запроса 400 раз в MySQL.

Но - как избавиться от этой ошибки?

NoMethodError (private method `rand' called for #<User::ActiveRecord_Relation:0x007fbab27d7ea8>): 

Спасибо заранее

+0

Сколько строк имеет эта таблица в целом? Являются ли основные идентификаторы в этой таблице завершены или имеются пробелы (вы удалили записи)? – spickermann

ответ

2

Я хотел бы добавить следующий объем модели (в зависимости от используемой базы данных):

# to model/model.rb 
# 'RANDOM' works with postgresql and sqlite, whereas mysql uses 'RAND' 
scope :random, -> { order('RAND()') } 

Тогда следующий запрос будет загружать случайное число (в диапазоне 200-400) объектов в один запрос:

Model.random.limit(rand(200...400)) 

Если вы действительно хотите сделать это в Rails, а не в базе данных, а затем загрузить все записи и использовать sample:

Model.all.sample(rand(200..400)) 

Но что будет медленнее (в зависимости от количества записей в базе данных), так как Rails будет загружать все записи из базы данных и создавать их, что может занять массу памяти.

+0

Но это означает, что я всегда получаю 400 записей, не так ли? Мне понадобится случайное количество результатов (скажем, от 200 до 400). Кроме того, в базе данных 400 запросов, но в другом подходе имеется только одна запись и 400 раз запускается метод захвата случайных записей. Просто интересно, что более эффективно. – user984621

+0

ORDER BY может отрицательно влиять на производительность запросов в больших таблицах базы данных. Я бы не использовал это в производственном коде. – mcfinnigan

+0

@ user984621: Я обновил свой ответ. Он будет запрашивать базу данных только один раз. – spickermann

0

Использование data.sample(rand(100..200)) для получения дополнительной информации, почему рэнд не работает, читайте здесь https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/4555

+0

Хмм .. Я пытаюсь использовать этот подход, но получил эту ошибку: 'TypeError (без неявного преобразования Range в Integer):' – user984621

+0

вы можете попробовать data.sample (rand (10..100)) –

0

Другой способ, который не является DB специфичный:

def self.random_record 
    self.where('something = ? and id = ?', "something", rand(self.count)) 
end 

Единственное, что здесь - 2 запросов выполняются. self.count делает один запрос - SELECT COUNT(*) FROM models, а другой - ваш фактический запрос, чтобы получить случайный запись.

Ну, теперь предположим, что вы хотите n случайных записей. Тогда напишите это нравится:

def self.random_records n 
    records = self.count 
    rand_ids = Array.new(n) { rand(records) } 
    self.where('something = ? and id IN (?)', 
      "something", rand_ids) 
end 
0

Это действительно зависит от того, сколько усилий вы хотите поместить в оптимизации этого, потому что есть более чем одно решение. Вот два варианта.

Что-то простое - использовать ORDER BY RAND() LIMIT 400, чтобы случайным образом выбрать 400 предметов.

Или просто выбрать все под луной, а затем использовать Ruby, чтобы случайно выбрать 400 из общего набора результатов, например:

data = Model.where(something: 'something').all # all is necessary to exec query 
400.times do 
    data.sample # returns a random model 
end 

Я не рекомендовал бы второй метод, но он должен работать.

Смежные вопросы