Мне нужно получить случайную запись из таблицы через ActiveRecord. Я последовал примеру Джемиса Бака из 2006 года .
Тем не менее, я также натолкнулся на другой способ поиска в Google (не могу приписать ссылку из-за ограничений нового пользователя):
rand_id = rand(Model.count)
rand_record = Model.first(:conditions => ["id >= ?", rand_id])
Мне любопытно, как другие здесь сделали это, или если кто-нибудь знает, какой путь будет более эффективным.
ruby-on-rails
random
rails-activerecord
jyunderwood
источник
источник
Ответы:
Я не нашел идеальный способ сделать это без по крайней мере двух запросов.
Далее в качестве смещения используется случайно сгенерированное число (до текущего количества записей) .
Если честно, я только что использовал ORDER BY RAND () или RANDOM () (в зависимости от базы данных). Это не проблема производительности, если у вас нет проблем с производительностью.
источник
Model.find(:offset => offset).first
выдаст ошибку. Я думаю,Model.first(:offset => offset)
может работать лучше.Thing.order("RANDOM()").limit(100)
для 100 случайно выбранных записей. (Имейте в виду, что этоRANDOM()
в PostgreSQL иRAND()
в MySQL ... не так переносимо, как хотелось бы.)Model.offset(offset).first
.Рельсы 6
Как заявил Джейсон в комментариях, в Rails 6 неатрибутные аргументы не допускаются. Вы должны заключить значение в
Arel.sql()
утверждение.Рельсы 5, 4
В Rails 4 и 5 , используя Postgresql или SQLite , используя
RANDOM()
:Предположительно то же самое будет работать для MySQL с
RAND()
Это примерно в 2,5 раза быстрее, чем подход в принятом ответе .
Предостережение : это медленно для больших наборов данных с миллионами записей, поэтому вы можете добавить
limit
предложение.источник
Ваш пример кода начнет вести себя неточно после удаления записей (он будет несправедливо отдавать предпочтение элементам с более низким идентификатором)
Возможно, вам лучше использовать случайные методы в вашей базе данных. Они различаются в зависимости от того, какую БД вы используете, но: order => "RAND ()" работает для mysql, а: order => "RANDOM ()" работает для postgres
источник
Model.order("RANDOM()").first
вместо этого.Сравнительный анализ этих двух методов в MySQL 5.1.49, Ruby 1.9.2p180 в таблице продуктов с + 5 миллионами записей:
Смещение в MySQL выглядит намного медленнее.
РЕДАКТИРОВАТЬ Я также пытался
Но мне пришлось убить его через ~ 60 секунд. MySQL был "Копирование в таблицу tmp на диске". Это не сработает.
источник
Thing.order("RANDOM()").first
таблицу с 250 тыс. Записей - запрос завершился менее чем за полсекунды. (PostgreSQL 9.0, REE 1.8.7, 2 x 2,66 ГГц ядра) Это достаточно быстро для меня, так как я делаю одноразовую «очистку».rand_id = rand(Product.count) + 1
или никогда не получите последнюю запись.random1
не будет работать, если вы удалите строку в таблице. (Количество будет меньше максимального идентификатора, и вы никогда не сможете выбрать строки с высоким идентификатором).random2
может быть улучшено#order
использованием индексированного столбца.Это не должно быть так сложно.
pluck
возвращает массив всех идентификаторов в таблице.sample
Метод на массив, возвращает случайное идентификатор из массива.Это должно работать хорошо, с равной вероятностью выбора и поддержкой таблиц с удаленными строками. Вы можете даже смешать это с ограничениями.
И тем самым выбирайте случайного пользователя, который любит пятницы, а не просто любого пользователя.
источник
Не рекомендуется использовать это решение, но если по какой-то причине вы действительно хотите случайно выбрать запись, выполняя только один запрос к базе данных, вы можете использовать
sample
метод из класса Ruby Array , который позволяет выбирать случайный элемент. из массива.Этот метод требует только запроса к базе данных, но он значительно медленнее, чем альтернативы,
Model.offset(rand(Model.count)).first
которые требуют двух запросов к базе данных, хотя последний все еще предпочтителен.источник
Я сделал рельсы 3 драгоценного камня, чтобы справиться с этим:
https://github.com/spilliton/randumb
Это позволяет вам делать такие вещи:
источник
ORDER BY RANDOM()
(илиRAND()
для mysql) к вашему запросу». - следовательно, комментарии о плохой производительности, упомянутые в комментариях к ответу @semanticart, также применимы при использовании этого драгоценного камня. Но, по крайней мере, это не зависит от БД.Я использую это так часто из консоли, я расширяю ActiveRecord в инициализаторе - пример Rails 4:
Затем я могу позвонить,
Foo.random
чтобы вернуть случайную запись.источник
limit(1)
?ActiveRecord#first
должен быть достаточно умен, чтобы сделать это.Один запрос в Postgres:
Используя смещение, два запроса:
источник
Чтение всего этого не давало мне уверенности в том, что из них лучше всего подойдет в моей конкретной ситуации с Rails 5 и MySQL / Maria 5.5. Итак, я проверил некоторые из ответов на ~ 65000 записей и получил два:
limit
явным победителем.pluck
+sample
.Этот ответ обобщает, проверяет и обновляет ответ Мохамеда , а также комментарий Нами ВАНГ к нему и комментарий Флориана Пилза о принятом ответе - пожалуйста, отправьте им голоса!
источник
Вы можете использовать
Array
методsample
, методsample
возвращает случайный объект из массива, для его использования вам просто нужно выполнить простойActiveRecord
запрос, который возвращает коллекцию, например:вернет что-то вроде этого:
источник
order('rand()').limit(1)
делает «ту же самую» работу (с ~ 10K записями).Настоятельно рекомендуем этот гем для случайных записей, который специально разработан для таблицы с большим количеством строк данных:
https://github.com/haopingfan/quick_random_records
Все остальные ответы плохо работают с большой базой данных, кроме этого гема:
4.6ms
полностью.User.order('RAND()').limit(10)
стоимость733.0ms
.offset
подход к ответу стоил245.4ms
полностью.User.all.sample(10)
затратный подход573.4ms
.Примечание. В моей таблице всего 120 000 пользователей. Чем больше у вас записей, тем больше будет разница в производительности.
источник
Если вам нужно выбрать несколько случайных результатов в указанной области :
источник
Метод Ruby для случайного выбора элемента из списка
sample
. Желая создать эффективнуюsample
для ActiveRecord и основываясь на предыдущих ответах, я использовал:Я вставляю это
lib/ext/sample.rb
и затем загружаю это с этим вconfig/initializers/monkey_patches.rb
:Это будет один запрос, если размер модели уже кэширован, и два в противном случае.
источник
Rails 4.2 и Oracle :
Для оракула вы можете установить область действия вашей модели следующим образом:
или
А затем для примера вызовите это так:
или
Конечно, вы также можете разместить заказ без объема:
источник
order('random()'
и MySQLorder('rand()')
. Это определенно лучший ответ.Для базы данных MySQL попробуйте: Model.order ("RAND ()"). First
источник
Если вы используете PostgreSQL 9.5+, вы можете воспользоваться
TABLESAMPLE
выбрать случайную запись.Два метода выборки по умолчанию (
SYSTEM
иBERNOULLI
) требуют, чтобы вы указали количество возвращаемых строк в процентах от общего числа строк в таблице.Это требует знания количества записей в таблице, чтобы выбрать соответствующий процент, который может быть нелегко найти быстро. К счастью, есть
tsm_system_rows
модуль, который позволяет вам указать количество строк, которые будут возвращены напрямую.Чтобы использовать это в ActiveRecord, сначала включите расширение в миграции:
Затем измените
from
условие запроса:Я не знаю, будет ли
SYSTEM_ROWS
метод выборки полностью случайным или он просто возвращает первую строку со случайной страницы.Большая часть этой информации была взята из блога 2ndQuadrant, написанного Gulcin Yildirim .
источник
Увидев так много ответов, я решил сравнить их все в моей базе данных PostgreSQL (9.6.3). Я использую меньшую таблицу из 100 000 и сначала избавился от Model.order ("RANDOM ()"). Так как он был уже на два порядка медленнее.
Используя таблицу с 2 500 000 записей с 10 столбцами, победителем хендсайда был метод срыва, который был почти в 8 раз быстрее, чем занявший второе место (смещение. Я запустил это только на локальном сервере, так что число может быть завышено, но его достаточно больше, чтобы получить Метод - это то, чем я в конечном итоге воспользуюсь. Стоит также отметить, что это может вызвать проблемы, если вы выбираете более одного результата за раз, поскольку каждый из них будет уникальным или менее случайным.
Pluck выигрывает 100 раз за мою таблицу с 25 000 000 правками. Edit: на самом деле, это время включает в себя pluck в цикле, если я его вычеркну, он выполняется примерно так же быстро, как и итерация по id. Тем не мение; это занимает довольно много оперативной памяти.
Вот данные, бегущие 2000 раз в моей таблице строк на 100 000, чтобы исключить случайное
источник
Очень старый вопрос, но с:
Вы получили массив записей, отсортированный по случайному порядку. Не нужно драгоценных камней или сценариев.
Если вы хотите одну запись:
источник
shuffle.first
==.sample
Я новичок в RoR, но у меня это получилось:
Это пришло от:
Как случайным образом отсортировать (скремблировать) массив в Ruby?
источник
array.shuffle
. В любом случае, будьте осторожны, так какCard.all
все записи карточки будут загружены в память, которая становится все более неэффективной, чем больше объектов, о которых мы говорим.Что делать?
Для меня это очень понятно
источник
Я пытаюсь использовать этот пример Сэма в моем приложении, используя rails 4.2.8 из Benchmark (я помещаю 1..Category.count для случайного числа, потому что если случайное значение принимает значение 0, это приведет к ошибке (ActiveRecord :: RecordNotFound: Не удалось найти Категория с 'id' = 0)) и моя была:
источник
.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
условия выбираются до применения условий.Другое решение для больших таблиц (но не очень случайное):
где
sample_size
может быть100
(но не слишком большим, иначе он медленный и потребляет много памяти), иlimit
может быть1
. Обратите внимание, что, хотя это быстро, но на самом деле не случайно, оно случайноsample_size
только в записях.PS: результаты тестов в ответах выше не являются надежными (по крайней мере, в Postgres), потому что некоторые запросы к БД, выполняющиеся во 2-й раз, могут быть значительно быстрее, чем в 1-й раз, благодаря кешу БД. И, к сожалению, в Postgres нет простого способа отключить кэш, чтобы сделать эти тесты надежными.
источник
Наряду с использованием
RANDOM()
, вы также можете бросить это в область видимости:Или, если вам это не нравится, просто добавьте его в метод класса. Теперь
Thing.random
работает вместе сThing.random(n)
.источник
В зависимости от значения «случайный» и того, что вы на самом деле хотите сделать,
take
может быть достаточно.Под «значением» случайного я подразумеваю:
Например, для тестирования образцы данных могли быть созданы в любом случае случайным образом, так
take
что этого более чем достаточно, и, если честно, дажеfirst
.https://guides.rubyonrails.org/active_record_querying.html#take
источник