.order('RANDOM()').limit(limit)
wygląda schludnie, ale działa wolno w przypadku dużych tabel, ponieważ musi pobrać i posortować wszystkie wiersze, nawet jeśli limit
ma wartość 1 (wewnętrznie w bazie danych, ale nie w Railsach). Nie jestem pewien co do MySQL, ale dzieje się tak w Postgres. Więcej wyjaśnień tutaj i tutaj .
Jednym z rozwiązań dla dużych stołów jest .from("products TABLESAMPLE SYSTEM(0.5)")
to, co 0.5
oznacza 0.5%
. Jednak uważam, że to rozwiązanie jest nadal powolne, jeśli masz WHERE
warunki, które odfiltrowują wiele wierszy. Wydaje mi się, że to dlatego, że wcześniej TABLESAMPLE SYSTEM(0.5)
pobierz wszystkie wierszeWHERE
zastosowaniem warunków.
Innym rozwiązaniem dla dużych tabel (ale niezbyt losowych) jest:
products_scope.limit(sample_size).sample(limit)
gdzie sample_size
może być 100
(ale nie za duży, w przeciwnym razie jest powolny i zużywa dużo pamięci) i limit
może być 1
. Zauważ, że chociaż jest to szybkie, ale nie jest to przypadkowe, jest losowe sample_size
tylko w rekordach.
PS: Wyniki testów porównawczych w powyższych odpowiedziach nie są wiarygodne (przynajmniej w Postgres), ponieważ niektóre zapytania DB uruchomione po raz drugi mogą być znacznie szybsze niż uruchomione za pierwszym razem, dzięki pamięci podręcznej DB. I niestety nie ma łatwego sposobu na wyłączenie pamięci podręcznej w Postgres, aby te testy były wiarygodne.