Итак, я нашел несколько примеров для поиска случайной записи в Rails 2 - предпочтительный метод выглядит следующим образом:
Thing.find :first, :offset => rand(Thing.count)
Будучи чем-то новичком, я не уверен, как это можно построить с помощью нового синтаксиса find в Rails 3.
Итак, что такое "Rails 3 Way", чтобы найти случайную запись?
Ответ 1
Thing.first(:order => "RANDOM()") # For MySQL :order => "RAND()", - thanx, @DanSingerman
# Rails 3
Thing.order("RANDOM()").first
или
Thing.first(:offset => rand(Thing.count))
# Rails 3
Thing.offset(rand(Thing.count)).first
Собственно, в Rails 3 будут работать все примеры. Но использование порядка RANDOM
довольно медленное для больших таблиц, но больше sql-style
UPD. Вы можете использовать следующий трюк в индексированном столбце (синтаксис PostgreSQL):
select *
from my_table
where id >= trunc(
random() * (select max(id) from my_table) + 1
)
order by id
limit 1;
Ответ 2
Я работаю над проектом (Rails 3.0.15, ruby 1.9.3-p125-perf), где db находится в localhost, а таблица пользователей имеет бит более записей 100K.
Используя
по RAND()
довольно медленный
User.order( "RAND (идентификатор)" ). Первая
становится
SELECT users
. * FROM users
ORDER BY RAND (id) LIMIT 1
и требуется от 8 до 12 секунд.
Журнал рельсов:
Пользовательская нагрузка (11030.8ms) SELECT users
. * FROM users
ORDER BY RAND() LIMIT 1
из mysql explain
+----+-------------+-------+------+---------------+------+---------+------+--------+---------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+--------+---------------------------------+
| 1 | SIMPLE | users | ALL | NULL | NULL | NULL | NULL | 110165 | Using temporary; Using filesort |
+----+-------------+-------+------+---------------+------+---------+------+--------+---------------------------------+
Вы можете видеть, что индекс не используется (possible_keys = NULL), создается временная таблица и требуется дополнительный проход для получения желаемого значения ( extra = Использование временного; FileSort).
С другой стороны, разделяя запрос на две части и используя Ruby, мы имеем разумное улучшение времени отклика.
users = User.scoped.select(:id);nil
User.find( users.first( Random.rand( users.length )).last )
(; nil для использования в консоли)
Журнал рельсов:
Пользовательская загрузка (25,2 мс) SELECT id FROM users
Пользовательская нагрузка (0.2ms) SELECT users
. * FROM users
ГДЕ users
. id
= 106854 LIMIT 1
и mysql объясняют, почему:
+----+-------------+-------+-------+---------------+--------------------------+---------+------+--------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+--------------------------+---------+------+--------+-------------+
| 1 | SIMPLE | users | index | NULL | index_users_on_user_type | 2 | NULL | 110165 | Using index |
+----+-------------+-------+-------+---------------+--------------------------+---------+------+--------+-------------+
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
| 1 | SIMPLE | users | const | PRIMARY | PRIMARY | 4 | const | 1 | |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
теперь мы можем использовать только индексы и первичный ключ и выполнять работу примерно в 500 раз быстрее!
UPDATE:
как указано icantbecool в комментариях, вышеупомянутое решение имеет недостаток, если в таблице удалены записи.
Обходной путь в этом случае может быть
users_count = User.count
User.scoped.limit(1).offset(rand(users_count)).first
который переводит на два запроса
SELECT COUNT(*) FROM `users`
SELECT `users`.* FROM `users` LIMIT 1 OFFSET 148794
и работает примерно в 500 мс.
Ответ 3
Я сделал рельсы 3 жемчужины для этого, что лучше работает на больших таблицах и позволяет цепочки отношений и областей:
https://github.com/spilliton/randumb
(edit): По умолчанию мой жемчуг по умолчанию использует тот же подход, что и выше, но вы можете использовать старый путь, если хотите:)
Ответ 4
При использовании Postgres
User.limit(5).order("RANDOM()")
Если вы используете MySQL
User.limit(5).order("RAND()")
В обоих случаях вы произвольно выбираете 5 записей из таблицы Users. Вот фактический запрос SQL, отображаемый в консоли.
SELECT * FROM users ORDER BY RANDOM() LIMIT 5
Ответ 5
здесь мы идем
рельсы
#in your initializer
module ActiveRecord
class Base
def self.random
if (c = count) != 0
find(:first, :offset =>rand(c))
end
end
end
end
Использование
Model.random #returns single random object
или вторая мысль
module ActiveRecord
class Base
def self.random
order("RAND()")
end
end
end
использование:
Model.random #returns shuffled collection
Ответ 6
Многие из опубликованных ответов фактически не будут хорошо работать на довольно больших таблицах (1 + миллион строк). Случайное упорядочение быстро занимает несколько секунд, и делать счет на столе также занимает довольно много времени.
Решение, которое хорошо работает для меня в этой ситуации, заключается в использовании RANDOM()
с условием where:
Thing.where('RANDOM() >= 0.9').take
В таблице с более чем миллионом строк этот запрос обычно занимает менее 2 мс.
Ответ 7
Это было очень полезно для меня, однако мне нужно было немного больше гибкости, так вот что я сделал:
Случай1: поиск одной случайной записи источник: trevor turk site
Добавьте это в модель Thing.rb
def self.random
ids = connection.select_all("SELECT id FROM things")
find(ids[rand(ids.length)]["id"].to_i) unless ids.blank?
end
то в вашем контроллере вы можете вызвать что-то вроде этого
@thing = Thing.random
Случай 2: поиск нескольких случайных записей (без повторов) источник: не могу вспомнить
Мне нужно было найти 10 случайных записей без повторов, поэтому я нашел работу
В контроллере:
thing_ids = Thing.find( :all, :select => 'id' ).map( &:id )
@things = Thing.find( (1..10).map { thing_ids.delete_at( thing_ids.size * rand ) } )
Это найдет 10 случайных записей, однако стоит упомянуть, что если база данных будет особенно большой (миллионы записей), это не будет идеальным, и производительность будет затруднена. Я буду исполнять до нескольких тысяч записей, которые были достаточными для меня.
Ответ 8
Метод Ruby для случайного выбора элемента из списка sample
. Желая создать эффективный sample
для ActiveRecord и на основе предыдущих ответов, я использовал:
module ActiveRecord
class Base
def self.sample
offset(rand(size)).first
end
end
end
Я помещаю это в lib/ext/sample.rb
, а затем загружаю его с помощью config/initializers/monkey_patches.rb
:
Dir[Rails.root.join('lib/ext/*.rb')].each { |file| require file }
Ответ 9
Работает в Rails 5 и является агностиком DB:
Это в вашем контроллере:
@quotes = Quote.offset(rand(Quote.count - 3)).limit(3)
Вы можете, конечно, поставить это в беспокойство, как показано здесь.
приложение/модели/проблемы/randomable.rb
module Randomable
extend ActiveSupport::Concern
class_methods do
def random(the_count = 1)
records = offset(rand(count - the_count)).limit(the_count)
the_count == 1 ? records.first : records
end
end
end
то...
приложение/модели/book.rb
class Book < ActiveRecord::Base
include Randomable
end
Затем вы можете просто использовать:
Books.random
или
Books.random(3)
Ответ 10
Вы можете использовать sample() в ActiveRecord
например.
def get_random_things_for_home_page
find(:all).sample(5)
end
Источник: http://thinkingeek.com/2011/07/04/easily-select-random-records-rails/
Ответ 11
Если используется Oracle
User.limit(10).order("DBMS_RANDOM.VALUE")
Выход
SELECT * FROM users ORDER BY DBMS_RANDOM.VALUE WHERE ROWNUM <= 10
Ответ 12
Очень простой способ получить несколько случайных записей из таблицы. Это делает 2 дешевых запроса.
Model.where(id: Model.pluck(:id).sample(3))
Вы можете изменить "3" на количество случайных записей, которые вы хотите.
Ответ 13
Я просто столкнулся с этой проблемой при разработке небольшого приложения, где я хотел выбрать случайный вопрос из моей БД. Я использовал:
@question1 = Question.where(:lesson_id => params[:lesson_id]).shuffle[1]
И это хорошо работает для меня. Я не могу говорить о том, как производительность для больших БД, поскольку это всего лишь небольшое приложение.