Na czym polega 'Rails 4 Way' polegający na znalezieniu pewnej liczby przypadkowych rekordów?
User.find(:all, :order => "RANDOM()", :limit => 10)
tak zrobiłem w Rails 3.
User.all(:order => "RANDOM()", :limit => 10)
tak myślałem, że Rails 4 to zrobi, ale to wciąż daje mi ostrzeżenie o Deprecacji:
DEPRECATION WARNING: Relation#all is deprecated. If you want to eager-load a relation, you can call #load (e.g. `Post.where(published: true).load`). If you want to get an array of records from a relation, you can call #to_a (e.g. `Post.where(published: true).to_a`).
10 answers
Zamiast tego będziesz chciał użyć metod order
i limit
. Możesz pozbyć się all
.
Dla PostgreSQL i SQLite:
User.order("RANDOM()").limit(10)
Lub dla MySQL:
User.order("RAND()").limit(10)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-05 02:58:19
Ponieważ funkcja losowa może się zmieniać dla różnych baz danych, polecam użycie następującego kodu:
User.offset(rand(User.count)).first
Oczywiście jest to przydatne tylko wtedy, gdy szukasz tylko jednego rekordu.
Jeśli Chcesz Dostać więcej tego, możesz zrobić coś w stylu:
User.offset(rand(User.count) - 10).limit(10)
- 10
ma zapewnić, że otrzymasz 10 rekordów w przypadku, gdy rand zwróci liczbę większą niż count - 10.
pamiętaj, że zawsze otrzymasz 10 kolejnych rekordów.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-14 22:53:01
Myślę, że najlepszym rozwiązaniem jest tak naprawdę zamawianie losowo w bazie danych.
Ale jeśli chcesz uniknąć konkretnej funkcji losowej z bazy danych, możesz użyć podejścia pluck
i shuffle
.
Dla jednego rekordu:
User.find(User.pluck(:id).shuffle.first)
Dla więcej niż jednego rekordu:
User.where(id: User.pluck(:id).sample(10))
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-21 08:44:09
Sugerowałbym zrobienie tego zakresu, jak można go potem łańcuchować:
class User < ActiveRecord::Base
scope :random, -> { order(Arel::Nodes::NamedFunction.new('RANDOM', [])) }
end
User.random.limit(10)
User.active.random.limit(10)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-16 21:03:31
Choć nie jest to najszybsze rozwiązanie, Lubię zwięzłość:
User.ids.sample(10)
Metoda .ids
daje tablicę identyfikatorów użytkownika i .sample(10)
wybiera 10 losowych wartości z tej tablicy.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-01 09:36:26
Zdecydowanie polecam ten gem dla losowych rekordów, który jest specjalnie zaprojektowany dla tabeli z dużą ilością wierszy danych:
Https://github.com/haopingfan/quick_random_records
Wszystkie inne odpowiedzi działają źle z dużą bazą danych, z wyjątkiem tego klejnotu:
- quick_random_records kosztuje tylko
4.6ms
całkowicie.
- zaakceptowana odpowiedź
User.order('RAND()').limit(10)
koszt733.0ms
.
- the
offset
approach cost245.4ms
totally.
- koszt podejścia
User.all.sample(10)
573.4ms
.
Uwaga: mój stół ma tylko 120 000 użytkowników. Im więcej rekordów masz, tym większa będzie różnica w wydajności.
Aktualizacja:
Wykonaj na tabeli z 550 000 wierszy
-
Model.where(id: Model.pluck(:id).sample(10))
koszt1384.0ms
-
gem: quick_random_records
tylko koszt6.4ms
całkowicie
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-29 09:04:52
Dla MYSQL to mi pomogło:
User.order("RAND()").limit(10)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-19 13:29:47
Możesz zadzwonić .sample
na płytach, jak: User.all.sample(10)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-11-03 13:56:43
Odpowiedź @ maurimiranda User.offset(rand(User.count)).first
nie jest dobra, jeśli potrzebujemy 10 losowych rekordów, ponieważ User.offset(rand(User.count) - 10).limit(10)
zwróci sekwencję 10 rekordów z losowej pozycji, nie są one "Total random", prawda? Więc musimy wywołać tę funkcję 10 razy, aby uzyskać 10 "total losowo".
Poza tym offset nie jest również dobry, jeśli funkcja losowa zwraca wysoką wartość. Jeśli Twoje zapytanie wygląda jak offset: 10000 i limit: 20, generuje 10 020 wierszy i wyrzuca pierwsze 10 000 z oni, co jest bardzo drogie. Więc zadzwoń 10 razy offset.limit nie jest skuteczny.
Więc pomyślałem, że w przypadku, gdy chcemy tylko jednego losowego użytkownika, to User.offset(rand(User.count)).first
może lepiej (przynajmniej możemy poprawić buforowanie użytkownika.hrabia).
Ale jeśli chcemy 10 losowych użytkowników lub więcej, to User.order("RAND()").limit(10)
powinno być lepiej.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-02-11 10:54:58
Oto szybkie rozwiązanie.. obecnie używa go z ponad 1,5 miliona rekordów i uzyskuje przyzwoitą wydajność. Najlepszym rozwiązaniem byłoby buforowanie jednego lub więcej losowych zestawów rekordów, a następnie odświeżanie ich za pomocą workera w tle w pożądanym przedziale czasowym.
Utworzony random_records_helper.rb
Plik:
module RandomRecordsHelper
def random_user_ids(n)
user_ids = []
user_count = User.count
n.times{user_ids << rand(1..user_count)}
return user_ids
end
W kontrolerze:
@users = User.where(id: random_user_ids(10))
Jest to znacznie szybsze niż metoda .order("RANDOM()").limit(10)
- przeszedłem z czasu ładowania 13 S do 500ms.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-07 15:37:42