Laravel - elokwentny lub płynny losowy rząd

242

Jak mogę wybrać losowy wiersz za pomocą Eloquent lub Fluent w frameworku Laravel?

Wiem, że używając SQL, możesz zrobić porządek za pomocą RAND (). Chciałbym jednak uzyskać losowy wiersz bez liczenia liczby rekordów przed początkowym zapytaniem.

Jakieś pomysły?

DigitalWM
źródło
Nie ma najlepszego sposobu, aby to zrobić bez wykonania co najmniej dwóch zapytań.
NARKOZ,

Odpowiedzi:

584

Laravel> = 5,2:

User::all()->random();
User::all()->random(10); // The amount of items you wish to receive

lub

User::inRandomOrder()->get();

lub uzyskać określoną liczbę rekordów

//5 indicates the number of records
User::inRandomOrder()->limit(5)->get();

Laravel 4.2.7 - 5.1:

User::orderByRaw("RAND()")->get();

Laravel 4.0 - 4.2.6:

User::orderBy(DB::raw('RAND()'))->get();

Laravel 3:

User::order_by(DB::raw('RAND()'))->get();

Sprawdź ten artykuł na temat losowych wierszy MySQL. Laravel 5.2 obsługuje to, w przypadku starszych wersji nie ma lepszego rozwiązania niż używanie zapytań RAW .

edycja 1: Jak wspomniano w Double Gras, orderBy () nie zezwala na nic innego niż ASC lub DESC od tej zmiany. Zaktualizowałem odpowiednio swoją odpowiedź.

edycja 2: Laravel 5.2 wreszcie implementuje w tym celu funkcję otoki . Nazywa się on inRandomOrder () .

Aebersold
źródło
81
Zamień „get” na „first”, jeśli chcesz mieć pojedynczy wiersz.
Collin Price
14
do użytku PostgreSQL'RANDOM()'
dwenaus
2
Ostrzeżenie: w przypadku dużych zestawów danych jest to bardzo wolne, dodając dla mnie około 900 ms
S ..
3
Czy możemy to podzielić na strony?
Irfandi D. Vendy
3
Możesz jednak sortować losowo na każdej nowej stronie. To nie ma sensu, ponieważ jest to w zasadzie to samo, co naciśnięcie F5.
aebersold
49

To działa dobrze,

$model=Model::all()->random(1)->first();

możesz także zmienić argument w funkcji losowej, aby uzyskać więcej niż jeden rekord.

Uwaga: niezalecane, jeśli masz ogromne dane, ponieważ najpierw pobierze wszystkie wiersze, a następnie zwróci losową wartość.

maniakalny
źródło
61
Minusem wydajności jest to, że wszystkie rekordy są pobierane.
Gras Double
3
tutaj losowe jest wywoływane w obiekcie kolekcji, a nie w zapytaniu sql. funkcja losowa jest uruchamiana po stronie php
astroanu
@astroanu Racja, ale aby zapełnić tę kolekcję, wszystkie wiersze są sprawdzane.
MetalFrog
1
Mogę się mylić, ale wydaje się, że to nie działa, gdy parametr przekazany do funkcji losowej jest taki sam jak rozmiar kolekcji.
Brynn Bateman,
To nie jest dobre ... W ten sposób odzyskujesz wszystkie rekordy i dostajesz losowy. Jeśli Twoja tabela ma zbyt wiele rekordów, może to być złe dla Twojej aplikacji.
Anderson Silva,
34

tl; dr: Obecnie jest zaimplementowany w Laravel, patrz „edycja 3” poniżej.


Niestety, na dzień dzisiejszy istnieją pewne zastrzeżenia dotyczące ->orderBy(DB::raw('RAND()'))proponowanego rozwiązania:

  • To nie jest DB-agnostyk. np. użycie SQLite i PostgreSQLRANDOM()
  • Co gorsza, to rozwiązanie nie ma już zastosowania, ponieważ ta zmiana :

    $direction = strtolower($direction) == 'asc' ? 'asc' : 'desc';


edycja: Teraz możesz użyć metody orderByRaw () :->orderByRaw('RAND()') . Jednak nadal nie jest to agnostyk DB.

FWIW, CodeIgniter implementuje specjalną RANDOM kierunek sortowania, który jest zastępowany poprawną gramatyką podczas budowania zapytania. Wydaje się również, że jest dość łatwy do wdrożenia. Wygląda na to, że mamy kandydata na ulepszenie Laravela :)

aktualizacja: tutaj jest problem na GitHub i moje oczekujące żądanie ściągnięcia .


edycja 2: Wytnijmy pościg. Od wersji Laravel 5.1.18 możesz dodawać makra do konstruktora zapytań:

use Illuminate\Database\Query\Builder;

Builder::macro('orderByRandom', function () {

    $randomFunctions = [
        'mysql'  => 'RAND()',
        'pgsql'  => 'RANDOM()',
        'sqlite' => 'RANDOM()',
        'sqlsrv' => 'NEWID()',
    ];

    $driver = $this->getConnection()->getDriverName();

    return $this->orderByRaw($randomFunctions[$driver]);
});

Stosowanie:

User::where('active', 1)->orderByRandom()->limit(10)->get();

DB::table('users')->where('active', 1)->orderByRandom()->limit(10)->get();


edycja 3: Wreszcie! Od wersji Laravel 5.2.33 (dziennik zmian , PR # 13642 ) możesz używać metody natywnej inRandomOrder():

User::where('active', 1)->inRandomOrder()->limit(10)->get();

DB::table('users')->where('active', 1)->inRandomOrder()->limit(10)->get();
Gras Double
źródło
Powinieneś zmienić nazwę makra 5.1 na inRandomOrder, aby była kompatybilna z przyszłością;) szczegóły, szczegóły :)
Sander Visser
To jest dokładnie jedna rzecz, którą zrobiłem podczas przygotowywania projektu 5.1 przed migracją do wersji 5.2.
Gras Double
To świetna odpowiedź. Gdybym mógł wybrać odpowiedź, zrobiłbym to!
mwallisch
18

W Laravel 4 i 5order_by otrzymuje brzmienie :orderBy

Powinno to być:

User::orderBy(DB::raw('RAND()'))->get();
Teodor Talow
źródło
User :: orderBy (DB :: raw ('RAND ()')) -> get ();
Dariusz
1
Działa dzięki, ale czy możesz podać jakieś informacje, jak to działa?
alayli
Czy możesz być trochę bardziej szczegółowy? Jakie informacje?
Teodor Talov
17

Możesz użyć :

ModelName::inRandomOrder()->first();
simhumileco
źródło
9

Dla Laravela 5.2> =

użyj Elokwentnej metody:

inRandomOrder()

Do losowego sortowania wyników zapytania można użyć metody inRandomOrder. Na przykład możesz użyć tej metody, aby pobrać losowego użytkownika:

$randomUser = DB::table('users')
            ->inRandomOrder()
            ->first();

z dokumentów: https://laravel.com/docs/5.2/queries#ordering-grouping-limit-and-offset

Manuel Azar
źródło
Course :: inRandomOrder () -> take (20) -> get (); Nie działa dla mnie - zła specyfikacja sortowania w linii Find.php 219
MJ
1
Przydaje się to w fabrykach modeli lub siewie
bazy danych
8

Możesz także użyć metody order_by z płynnym i wymownym, takim jak:

Posts::where_status(1)->order_by(DB::raw(''),DB::raw('RAND()')); 

To trochę dziwne użycie, ale działa.

Edycja: Jak powiedział @Alex, to użycie jest czystsze i działa również:

Posts::where_status(1)->order_by(DB::raw('RAND()'));
Bilal Gultekin
źródło
3
to również działa i jest trochę czystsze .. -> order_by (\ DB :: raw ('RAND ()'))
Alex Naspo 24.01.2013
3

Użyj funkcji Laravela

ModelName::inRandomOrder()->first();
Kamlesh Paul
źródło
3

Możesz łatwo użyć tego polecenia:

// Pytanie: nazwa modelu
// weź 10 wierszy z DB W losowych rekordach ...

$questions = Question::orderByRaw('RAND()')->take(10)->get();
hosein azimi
źródło
3

Wolę określić jako pierwszy lub nie:

$collection = YourModelName::inRandomOrder()
  ->firstOrFail();
giovannipds
źródło
3

Laravel ma wbudowaną metodę zmiany kolejności wyników.

Oto cytat z dokumentacji:

shuffle()

Metoda losowania losowo przetasowuje elementy w kolekcji:

$collection = collect([1, 2, 3, 4, 5]);

$shuffled = $collection->shuffle();

$shuffled->all();

// [3, 2, 5, 1, 4] - (generated randomly)

Tutaj możesz zobaczyć dokumentację .

AlmostPitt
źródło
2

Dodaj do swojego modelu:

public function scopeRandomize($query, $limit = 3, $exclude = [])
{
    $query = $query->whereRaw('RAND()<(SELECT ((?/COUNT(*))*10) FROM `products`)', [$limit])->orderByRaw('RAND()')->limit($limit);
    if (!empty($exclude)) {
        $query = $query->whereNotIn('id', $exclude);
    }
    return $query;
}

następnie na trasie / kontrolerze

$data = YourModel::randomize(8)->get();
Neto
źródło
2

Istnieje również whereRaw('RAND()')który robi to samo, można wtedy łańcuch ->get()lub ->first()nawet zaszaleć i dodać ->paginate(int).

ctf0
źródło
0

Mam tabelę z tysiącami rekordów, więc potrzebuję czegoś szybko. To jest mój kod dla pseudolosowego wiersza:

// count all rows with flag active = 1
$count = MyModel::where('active', '=', '1')->count(); 

// get random id
$random_id = rand(1, $count - 1);  

// get first record after random id
$data = MyModel::where('active', '=', '1')->where('id', '>', $random_id)->take(1)->first(); 
Krzysztof Chełchowski
źródło
Problem polega na tym, że jeśli istnieje wiele wierszy o identyfikatorach większych niż $counttylko pierwszy z nich byłby kiedykolwiek odzyskany, a zatem bardziej prawdopodobne jest, że zostanie on pobrany niż jakikolwiek inny wiersz.
kemika